diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..3729ff0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..2cbf89f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# CS1591: 缺少对公共可见类型或成员的 XML 注释
+dotnet_diagnostic.CS1591.severity = none
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1e0aaba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,345 @@
+## 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
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# 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
+
+# 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/
+
+# 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
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# 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
+# 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
+
+# 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/
+# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
+**/wwwroot/lib/
+
+# 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
+
+# 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/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# 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
+/WaterCloud.Web/DataProtection
+/WaterCloud.Web/wwwroot/file/local/20221006
+/WaterCloud.Web/watercloudnetdb.db-journal
+/RabbitMq
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cd4403d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 钱玮鸿
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1445ff2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+[](https://gitee.com/qian_wei_hong/WaterCloud/stargazers)
+[](https://gitee.com/qian_wei_hong/WaterCloud/members)
+
+
+
+
+
+
+
+----
+# WaterCloud
+
+#### 介绍
+
+- 请勿用于违反我国法律的项目上。
+- WaterCloud是一套基于ASP.NET 8.0 MVC + API + SqlSugar + LayUI的框架,源代码完全开源,可以帮助你解决C#.NET项目的重复工作!
+- 采用主流架构思想,容易上手,简单易学,学习成本低。
+- 可完全实现二次开发让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省人力成本,同时又不失灵活性。
+- 支持SQLServer、MySQL 等多数据库类型。模块化设计,层次结构清晰。内置一系列企业信息管理的基础功能。
+- 操作权限基于RBAC,权限控制精密细致,对所有管理链接都进行权限验证,可控制到导航菜单、功能按钮,控制到行级,列表级,表单字段级。
+- 数据权限,精细化数据权限控制,实现不同人看不同数据。
+- 代码生成功能,简单前后端代码生成。
+- 表单设计器,提供多种方式设计表单,动态表单拖拉式设计以及自定义表单。
+- 流程设计器,动态设计流程,节点及连线条件设计。
+- 内容管理,已配置好wangEditor编辑器,可以使用。
+- 文件管理,提供文件上传及下载功能。
+- 提高开发效率及质量。常用类封装,日志、缓存、验证、字典、文件、邮件、Excel。等等。
+- 页面为响应式设计,支持电脑、平板、智能手机等设备,微信浏览器以及各种常见浏览器。
+- 适用范围:可以开发OA、ERP、BPM、CRM、WMS、TMS、MIS、BI、电商平台后台、物流管理系统、快递管理系统、教务管理系统等各类管理软件。
+- 租户管理,基于Database的多租户功能(SqlSugar支持)。
+- 定时任务,基于quartz的定时任务功能(可以集群)。
+- 项目演示地址:http://47.116.127.212:5000/ (账号:admin 密码:0000,数据库2个小时还原一次)
+- 文档地址:https://gitee.com/qian_wei_hong/WaterCloud/wikis/pages
+- 在线项目地址:https://replit.com/@MonsterUncle/WaterCloud
+
+#### 前端以及后端使用技术介绍
+
+1、前端技术
+
+- js框架:jquery-3.4.1、LayUI、LayUI mini(开源)。
+- 图标:Font Awesome 4.7.0及LayUI自带。
+- 客户端验证:LayUI verify。
+- 富文本编辑器:开源wangEditor、LayUI editor。
+- 上传文件:LayUI upoload。
+- 动态页签:LayUI mini miniTab。
+- 数据表格:LayUI table、LayUI 开源 soul-table组件(已实现后端筛选)。
+- 下拉选择框:LayUI select、xmselect。
+- 树结构控件:LayUI 开源 dtree。
+- 树状表格:LayUI 开源 treetable-lay,框架改造treetable低版本(兼容soul-table组件、修复固定列等BUG)。
+- 穿梭框:LayUI transfer。
+- 页面布局:LayUI、LayUI mini。
+- 图表插件:echarts
+- 日期控件:LayUI laydate
+- 图标选择:LayUI 开源 IconPicker
+- 省市区选择:LayUI 开源 layarea
+
+2、后端技术
+
+- 核心框架:ASP.NET 8.0、WEB API
+- 定时任务:QuartZ,实现web控制
+- 持久层框架:SqlSugar(支持多种数据库,复杂查询操作、多租户、分库分表等)、Chloe(支持多种数据库,复杂查询操作,比较稳定)
+- 安全支持:过滤器、Sql注入、请求伪造
+- 服务端验证:实体模型验证
+- 缓存框架:Redis/Memory(单点登录控制)
+- 消息队列: RabbitMq
+- 事件总线: Jaina
+- 日志管理:Log、登录日志、操作日志
+- 工具类:MiniExcel、Newtonsoft.Json、验证码、丰富公共类
+- 其他:AutoFac、Swagger
+
+
+#### 环境要求
+
+1. VS2022及以上版本;
+2. Asp.net 8.0;
+3. Mysql或者SQLSERVER2005及以上版本,database文件夹下有sql文件可执行;
+4. 请使用VS2022及以上版本打开解决方案。
+5. Redis和RabbitMq在项目文件夹里有
+
+#### 友情链接
+
+1. 前端框架Layui 文档地址:https://layui.gitee.io/v2/
+2. Layui前端框架Layuimini 码云地址:https://gitee.com/zhongshaofa/layuimini
+3. SqlSugar.ORM 文档地址:https://www.donet5.com/home/doc
+4. WaterCloud讨论交流QQ群(1065447456)[](https://jq.qq.com/?_wv=1027&k=51RHQVG)
+5. .NET易用底层框架 Furion,码云地址:https://gitee.com/dotnetchina/Furion
+
+#### 捐赠支持
+
+开源项目不易,若此项目能得到你的青睐,可以捐赠支持作者持续开发与维护,感谢所有支持开源的朋友。
+
+
+ 
\ No newline at end of file
diff --git a/Redis/Redis-x64-3.2.100.msi b/Redis/Redis-x64-3.2.100.msi
new file mode 100644
index 0000000..6b7e300
Binary files /dev/null and b/Redis/Redis-x64-3.2.100.msi differ
diff --git a/Redis/Redis说明.docx b/Redis/Redis说明.docx
new file mode 100644
index 0000000..e42bd1a
Binary files /dev/null and b/Redis/Redis说明.docx differ
diff --git a/Redis/打开.jpg b/Redis/打开.jpg
new file mode 100644
index 0000000..a7bf86f
Binary files /dev/null and b/Redis/打开.jpg differ
diff --git a/WaterCloud.Code/Attribute/HandlerAdminAttribute.cs b/WaterCloud.Code/Attribute/HandlerAdminAttribute.cs
new file mode 100644
index 0000000..b384dc8
--- /dev/null
+++ b/WaterCloud.Code/Attribute/HandlerAdminAttribute.cs
@@ -0,0 +1,32 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 管理员特性,MVC使用
+ ///
+ public class HandlerAdminAttribute : ActionFilterAttribute
+ {
+ private readonly bool _isSuper;
+
+ public HandlerAdminAttribute(bool isSuper = true)
+ {
+ _isSuper = isSuper;
+ }
+
+ public override void OnActionExecuting(ActionExecutingContext filterContext)
+ {
+ if (OperatorProvider.Provider.GetCurrent() != null && _isSuper == true ? OperatorProvider.Provider.GetCurrent().IsSuperAdmin : OperatorProvider.Provider.GetCurrent().IsAdmin)
+ {
+ return;
+ }
+ else
+ {
+ //filterContext.HttpContext.Response.WriteAsync("");
+ filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.PathBase + "/Home/Error?msg=403");
+ return;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Attribute/HandlerAjaxOnlyAttribute.cs b/WaterCloud.Code/Attribute/HandlerAjaxOnlyAttribute.cs
new file mode 100644
index 0000000..bad2714
--- /dev/null
+++ b/WaterCloud.Code/Attribute/HandlerAjaxOnlyAttribute.cs
@@ -0,0 +1,34 @@
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.ActionConstraints;
+using Microsoft.AspNetCore.Routing;
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// ajax验证
+ ///
+ [AttributeUsage(AttributeTargets.Method)]
+ public class HandlerAjaxOnlyAttribute : ActionMethodSelectorAttribute
+ {
+ public bool Ignore { get; set; }
+
+ public HandlerAjaxOnlyAttribute(bool ignore = false)
+ {
+ Ignore = ignore;
+ }
+
+ public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
+ {
+ if (Ignore)
+ return true;
+ bool result = false;
+ var xreq = routeContext.HttpContext.Request.Headers.ContainsKey("x-requested-with");
+ if (xreq)
+ {
+ result = routeContext.HttpContext.Request.Headers.ContainsKey("x-requested-with");
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Attribute/HandlerLockAttribute.cs b/WaterCloud.Code/Attribute/HandlerLockAttribute.cs
new file mode 100644
index 0000000..566b897
--- /dev/null
+++ b/WaterCloud.Code/Attribute/HandlerLockAttribute.cs
@@ -0,0 +1,56 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 防重复锁,MVC使用
+ ///
+ public class HandlerLockAttribute : ActionFilterAttribute
+ {
+ public HandlerLockAttribute()
+ {
+ }
+
+ public override void OnActionExecuting(ActionExecutingContext filterContext)
+ {
+ if (OperatorProvider.Provider.GetCurrent() == null)
+ {
+ WebHelper.WriteCookie("WaterCloud_login_error", "overdue");
+ //filterContext.HttpContext.Response.WriteAsync("");
+ OperatorProvider.Provider.EmptyCurrent("pc_").GetAwaiter().GetResult();
+ filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.PathBase + "/Home/Error?msg=408");
+ return;
+ }
+ else
+ {
+ string token = filterContext.HttpContext.Request.Cookies["pc_" + GlobalContext.SystemConfig.TokenName];
+ string cacheToken = CacheHelper.GetAsync("pc_" + GlobalContext.SystemConfig.TokenName + "_" + OperatorProvider.Provider.GetCurrent().UserId + "_" + OperatorProvider.Provider.GetCurrent().LoginTime).GetAwaiter().GetResult();
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ filterContext.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "token不能空" });
+ return;
+ }
+ if (string.IsNullOrWhiteSpace(cacheToken))
+ {
+ filterContext.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "token不能空" });
+ return;
+ }
+ if (token != cacheToken)
+ {
+ filterContext.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "请求异常" });
+ return;
+ }
+ //固定加锁5秒
+ bool result = CacheHelper.SetNx(token, token, 5);
+ if (!result)
+ {
+ filterContext.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "请求太频繁,请稍后" });
+ return;
+ }
+ }
+ //随机值
+ base.OnActionExecuting(filterContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Attribute/LockAttribute.cs b/WaterCloud.Code/Attribute/LockAttribute.cs
new file mode 100644
index 0000000..fd04a6c
--- /dev/null
+++ b/WaterCloud.Code/Attribute/LockAttribute.cs
@@ -0,0 +1,52 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 防重复提交,api使用
+ ///
+ public class LockAttribute : ActionFilterAttribute
+ {
+ ///
+ /// 拦截
+ ///
+ ///
+ ///
+ ///
+ public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ Stopwatch sw = new Stopwatch();
+ sw.Start();
+ if (GlobalContext.SystemConfig.Debug == false)
+ {
+ if (OperatorProvider.Provider.GetCurrent() == null)
+ {
+ context.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "抱歉,没有操作权限" });
+ return;
+ }
+ else
+ {
+ string token = context.HttpContext.Request.Headers[GlobalContext.SystemConfig.TokenName].ParseToString();
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ context.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "token不能空" });
+ return;
+ }
+ //固定加锁5秒
+ bool result = CacheHelper.SetNx(token, token, 5);
+ if (!result)
+ {
+ context.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = "请求太频繁,请稍后" });
+ return;
+ }
+ }
+ }
+ await next();
+
+ sw.Stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Attribute/ServiceDescriptionAttribute.cs b/WaterCloud.Code/Attribute/ServiceDescriptionAttribute.cs
new file mode 100644
index 0000000..c408bda
--- /dev/null
+++ b/WaterCloud.Code/Attribute/ServiceDescriptionAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace WaterCloud.Code
+{
+ [AttributeUsage(AttributeTargets.Class, Inherited = true)]
+ public class ServiceDescriptionAttribute : Attribute
+ {
+ public string ClassDescription
+ {
+ get;
+ set;
+ }
+
+ private ServiceDescriptionAttribute()
+ {
+ }
+
+ public ServiceDescriptionAttribute(string classDescription)
+ {
+ ClassDescription = classDescription;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Cache/CacheHelper.cs b/WaterCloud.Code/Cache/CacheHelper.cs
new file mode 100644
index 0000000..10674b7
--- /dev/null
+++ b/WaterCloud.Code/Cache/CacheHelper.cs
@@ -0,0 +1,468 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ public abstract class BaseHelper : RedisHelper
+ { }
+
+ public abstract class HandleLogHelper : RedisHelper
+ { }
+
+ public class CacheHelper
+ {
+ private static string cacheProvider = GlobalContext.SystemConfig.CacheProvider;
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 缓存时长h
+ /// 是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)
+ ///
+ public static async Task SetAsync(string key, object value, int expiresIn = -1, bool isSliding = true)
+ {
+ return await SetBySecondAsync(key, value, expiresIn * 3600, isSliding);
+ }
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 缓存时长秒
+ /// 是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)
+ ///
+ public static async Task SetBySecondAsync(string key, object value, int expiresIn = -1, bool isSliding = true)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ if (expiresIn > 0)
+ {
+ await BaseHelper.SetAsync(key, value, expiresIn);
+ }
+ else
+ {
+ await BaseHelper.SetAsync(key, value);
+ }
+ return await ExistsAsync(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ if (expiresIn > 0)
+ {
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(expiresIn), isSliding);
+ }
+ else
+ {
+ MemoryCacheHelper.Set(key, value);
+ }
+ return await ExistsAsync(key);
+
+ default:
+ if (expiresIn > 0)
+ {
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(expiresIn), isSliding);
+ }
+ else
+ {
+ MemoryCacheHelper.Set(key, value);
+ }
+ return await ExistsAsync(key);
+ }
+ }
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 缓存时长秒
+ /// 是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)
+ ///
+ public static bool SetBySecond(string key, object value, int expiresIn = -1, bool isSliding = true)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ if (expiresIn > 0)
+ {
+ BaseHelper.Set(key, value, expiresIn);
+ }
+ else
+ {
+ BaseHelper.Set(key, value);
+ }
+ return Exists(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ if (expiresIn > 0)
+ {
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(expiresIn), isSliding);
+ }
+ else
+ {
+ MemoryCacheHelper.Set(key, value);
+ }
+ return Exists(key);
+
+ default:
+ if (expiresIn > 0)
+ {
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(expiresIn), isSliding);
+ }
+ else
+ {
+ MemoryCacheHelper.Set(key, value);
+ }
+ return Exists(key);
+ }
+ }
+
+ ///
+ /// 获取缓存
+ ///
+ /// 缓存Key
+ ///
+ public static async Task GetAsync(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return await BaseHelper.GetAsync(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.Get(key);
+
+ default:
+ return MemoryCacheHelper.Get(key);
+ }
+ }
+
+ ///
+ /// 获取缓存
+ ///
+ /// 缓存Key
+ ///
+ public static T Get(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return BaseHelper.Get(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.Get(key);
+
+ default:
+ return MemoryCacheHelper.Get(key);
+ }
+ }
+
+ ///
+ /// 删除缓存
+ ///
+ /// 缓存Key
+ ///
+ public static async Task RemoveAsync(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ await BaseHelper.DelAsync(key);
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ MemoryCacheHelper.Remove(key);
+ break;
+ }
+ }
+
+ ///
+ /// 删除缓存
+ ///
+ /// 缓存Key
+ ///
+ public static void Remove(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ BaseHelper.Del(key);
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ MemoryCacheHelper.Remove(key);
+ break;
+ }
+ }
+
+ ///
+ /// 验证缓存项是否存在
+ ///
+ /// 缓存Key
+ ///
+ public static async Task ExistsAsync(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return await BaseHelper.ExistsAsync(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.Exists(key);
+
+ default:
+ return MemoryCacheHelper.Exists(key);
+ }
+ }
+
+ ///
+ /// 验证缓存项是否存在
+ ///
+ /// 缓存Key
+ ///
+ public static bool Exists(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException(nameof(key));
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return BaseHelper.Exists(key);
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.Exists(key);
+
+ default:
+ return MemoryCacheHelper.Exists(key);
+ }
+ }
+
+ ///
+ /// 缓存续期
+ ///
+ /// 缓存Key
+ /// 时间小时
+ ///
+ public static async Task ExpireAsync(string key, int hour)
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ await BaseHelper.ExpireAtAsync(key, DateTime.Now.AddHours(hour));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// 缓存续期
+ ///
+ /// 缓存Key
+ /// 时间小时
+ ///
+ public static void Expire(string key, int hour)
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ BaseHelper.ExpireAt(key, DateTime.Now.AddHours(hour));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// 清空缓存
+ ///
+ /// 缓存Key
+ ///
+ public static async Task FlushAllAsync()
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ await BaseHelper.NodesServerManager.FlushDbAsync();
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ MemoryCacheHelper.RemoveCacheAll();
+ break;
+
+ default:
+ MemoryCacheHelper.RemoveCacheAll();
+ break;
+ }
+ }
+
+ ///
+ /// 清空缓存
+ ///
+ /// 缓存Key
+ ///
+ public static void FlushAll1()
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ BaseHelper.NodesServerManager.FlushDb();
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ MemoryCacheHelper.RemoveCacheAll();
+ break;
+
+ default:
+ MemoryCacheHelper.RemoveCacheAll();
+ break;
+ }
+ }
+
+ ///
+ /// 不存在就插入
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 过期时间
+ ///
+ public static async Task SetNxAsync(string key, object value, int second = 10)
+ {
+ bool result = false;
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ result = await BaseHelper.SetNxAsync(key, value);
+ await BaseHelper.ExpireAtAsync(key, DateTime.Now.AddSeconds(second));
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ if (MemoryCacheHelper.Exists(key))
+ {
+ result = false;
+ MemoryCacheHelper.Get(key);
+ }
+ else
+ {
+ result = true;
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(second), true);
+ }
+ break;
+
+ default:
+ if (MemoryCacheHelper.Exists(key))
+ {
+ result = false;
+ MemoryCacheHelper.Get(key);
+ }
+ else
+ {
+ result = true;
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(second), true);
+ }
+ break;
+ }
+ return result;
+ }
+
+ ///
+ /// 不存在就插入
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 过期时间
+ ///
+ public static bool SetNx(string key, object value, int second = 10)
+ {
+ bool result = false;
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ result = BaseHelper.SetNx(key, value);
+ BaseHelper.ExpireAt(key, DateTime.Now.AddSeconds(second));
+ break;
+
+ case Define.CACHEPROVIDER_MEMORY:
+ if (MemoryCacheHelper.Exists(key))
+ {
+ result = false;
+ MemoryCacheHelper.Get(key);
+ }
+ else
+ {
+ result = true;
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(second), true);
+ }
+ break;
+
+ default:
+ if (MemoryCacheHelper.Exists(key))
+ {
+ result = false;
+ MemoryCacheHelper.Get(key);
+ }
+ else
+ {
+ result = true;
+ MemoryCacheHelper.Set(key, value, TimeSpan.FromSeconds(second), true);
+ }
+ break;
+ }
+ return result;
+ }
+
+ public static async Task> GetAllKeyAsync()
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return await BaseHelper.KeysAsync("SqlSugarDataCache.*");
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.GetCacheKeys();
+
+ default:
+ return MemoryCacheHelper.GetCacheKeys();
+ }
+ }
+
+ public static IEnumerable GetAllKey()
+ {
+ switch (cacheProvider)
+ {
+ case Define.CACHEPROVIDER_REDIS:
+ return BaseHelper.Keys("SqlSugarDataCache.*");
+
+ case Define.CACHEPROVIDER_MEMORY:
+ return MemoryCacheHelper.GetCacheKeys();
+
+ default:
+ return MemoryCacheHelper.GetCacheKeys();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Cache/MemoryCacheHelper.cs b/WaterCloud.Code/Cache/MemoryCacheHelper.cs
new file mode 100644
index 0000000..fbde7a0
--- /dev/null
+++ b/WaterCloud.Code/Cache/MemoryCacheHelper.cs
@@ -0,0 +1,244 @@
+using Microsoft.Extensions.Caching.Memory;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 缓存帮助类
+ ///
+ public class MemoryCacheHelper
+ {
+ private static readonly MemoryCache Cache = new MemoryCache(new MemoryCacheOptions());
+
+ ///
+ /// 验证缓存项是否存在
+ ///
+ /// 缓存Key
+ ///
+ public static bool Exists(string key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ return Cache.TryGetValue(key, out _);
+ }
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)
+ /// 绝对过期时长
+ ///
+ public static bool Set(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ Cache.Set(key, value.ToJson(),
+ new MemoryCacheEntryOptions().SetSlidingExpiration(expiresSliding)
+ .SetAbsoluteExpiration(expiressAbsoulte));
+ return Exists(key);
+ }
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ /// 缓存时长
+ /// 是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)
+ ///
+ public static bool Set(string key, object value, TimeSpan expiresIn, bool isSliding = false)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ Cache.Set(key, value.ToJson(),
+ isSliding
+ ? new MemoryCacheEntryOptions().SetSlidingExpiration(expiresIn)
+ : new MemoryCacheEntryOptions().SetAbsoluteExpiration(expiresIn));
+
+ return Exists(key);
+ }
+
+ ///
+ /// 添加缓存
+ ///
+ /// 缓存Key
+ /// 缓存Value
+ ///
+ public static bool Set(string key, object value)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ Cache.Set(key, value.ToJson());
+
+ return Exists(key);
+ }
+
+ #region 删除缓存
+
+ ///
+ /// 删除缓存
+ ///
+ /// 缓存Key
+ ///
+ public static void Remove(string key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+
+ Cache.Remove(key);
+ }
+
+ ///
+ /// 批量删除缓存
+ ///
+ ///
+ public static void RemoveAll(IEnumerable keys)
+ {
+ if (keys == null)
+ throw new ArgumentNullException(nameof(keys));
+
+ keys.ToList().ForEach(item => Cache.Remove(item));
+ }
+
+ #endregion 删除缓存
+
+ #region 获取缓存
+
+ ///
+ /// 获取缓存
+ ///
+ /// 缓存Key
+ ///
+ public static T Get(string key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ object temp;
+ if (Cache.TryGetValue(key, out temp))
+ {
+ return temp.ToString().ToObject();
+ }
+ return default(T);
+ }
+
+ ///
+ /// 获取缓存
+ ///
+ /// 缓存Key
+ ///
+ public static string Get(string key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ if (Cache.Get(key) == null)
+ {
+ return string.Empty;
+ }
+ return Cache.Get(key).ToString();
+ }
+
+ ///
+ /// 获取缓存集合
+ ///
+ /// 缓存Key集合
+ ///
+ public static IDictionary GetAll(IEnumerable keys)
+ {
+ if (keys == null)
+ throw new ArgumentNullException(nameof(keys));
+
+ var dict = new Dictionary();
+ keys.ToList().ForEach(item => dict.Add(item, Cache.Get(item)));
+ return dict;
+ }
+
+ #endregion 获取缓存
+
+ ///
+ /// 删除所有缓存
+ ///
+ public static void RemoveCacheAll()
+ {
+ var l = GetCacheKeys();
+ foreach (var s in l)
+ {
+ Remove(s);
+ }
+ }
+
+ ///
+ /// 删除匹配到的缓存
+ ///
+ ///
+ ///
+ public static void RemoveCacheRegex(string pattern)
+ {
+ IEnumerable l = SearchCacheRegex(pattern);
+ foreach (var s in l)
+ {
+ Remove(s);
+ }
+ }
+
+ ///
+ /// 搜索 匹配到的缓存
+ ///
+ ///
+ ///
+ public static IEnumerable SearchCacheRegex(string pattern)
+ {
+ var cacheKeys = GetCacheKeys();
+ var l = cacheKeys.Where(k => Regex.IsMatch(k, pattern)).ToList();
+ return l.AsReadOnly();
+ }
+
+ ///
+ /// 获取所有缓存键
+ ///
+ ///
+ public static List GetCacheKeys()
+ {
+#if NET8_0
+ const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
+ var entries = Cache.GetType().GetField("_coherentState", flags)?.GetValue(Cache);
+ var cacheItems = entries?.GetType()?.GetProperty("EntriesCollection", flags)?.GetValue(entries) as ICollection; //entries as IDictionary;
+ var keys = new List();
+ if (cacheItems == null) return keys;
+ foreach (var item in cacheItems)
+ {
+ var methodInfo = item.GetType().GetProperty("Key");
+
+ var val = methodInfo.GetValue(item);
+
+ keys.Add(val.ToString());
+ }
+ return keys;
+#else
+ const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
+ var entries = Cache.GetType().GetField("_entries", flags).GetValue(Cache);
+ var cacheItems = entries as IDictionary;
+ var keys = new List();
+ if (cacheItems == null) return keys;
+ foreach (DictionaryEntry cacheItem in cacheItems)
+ {
+ keys.Add(cacheItem.Key.ToString());
+ }
+ return keys;
+#endif
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DefaultStartUp.cs b/WaterCloud.Code/DefaultStartUp.cs
new file mode 100644
index 0000000..d32336a
--- /dev/null
+++ b/WaterCloud.Code/DefaultStartUp.cs
@@ -0,0 +1,368 @@
+using Autofac;
+using CSRedis;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.ResponseCompression;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.OpenApi.Models;
+using Serenity.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using WaterCloud.Code.Model;
+
+namespace WaterCloud.Code
+{
+ public class DefaultStartUp
+ {
+ protected IConfiguration Configuration { get; set; }
+ protected IWebHostEnvironment WebHostEnvironment { get; set; }
+
+ public DefaultStartUp(IConfiguration configuration, IWebHostEnvironment env)
+ {
+ Configuration = configuration;
+ WebHostEnvironment = env;
+ GlobalContext.LogWhenStart(env);
+ GlobalContext.HostingEnvironment = env;
+ }
+
+ public virtual void ConfigureServices(IServiceCollection services)
+ {
+ var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
+ //没有设置环境变量就默认生产环境
+ if (string.IsNullOrWhiteSpace(environment))
+ environment = "Production";
+ Configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true).Build();
+
+ GlobalContext.SystemConfig = Configuration.GetSection("SystemConfig").Get();
+ GlobalContext.Services = services;
+ GlobalContext.Configuration = Configuration;
+ services.Configure(options =>
+ {
+ options.CheckConsentNeeded = context => true;
+ options.MinimumSameSitePolicy = SameSiteMode.None;
+ });
+ //解决session问题
+ services.AddSession(options =>
+ {
+ options.Cookie.IsEssential = true;//是否强制存储cookie
+ options.Cookie.SameSite = SameSiteMode.None;
+ });
+ //代替HttpContext.Current
+ services.AddHttpContextAccessor();
+ //缓存缓存选择
+ if (GlobalContext.SystemConfig.CacheProvider != Define.CACHEPROVIDER_REDIS)
+ {
+ services.AddMemoryCache();
+ }
+ else
+ {
+ //redis 注入服务
+ string redisConnectiong = GlobalContext.SystemConfig.RedisConnectionString;
+ // 多客户端 1、基础 2、操作日志
+ var redisDB1 = new CSRedisClient(redisConnectiong + ",defaultDatabase=" + 0);
+ BaseHelper.Initialization(redisDB1);
+ var redisDB2 = new CSRedisClient(redisConnectiong + ",defaultDatabase=" + 1);
+ HandleLogHelper.Initialization(redisDB2);
+ services.AddSingleton(redisDB1);
+ services.AddSingleton(redisDB2);
+ }
+ //连续guid初始化,示例IDGen.NextId()
+ services.AddSingleton();
+ services.AddCors(options => options.AddPolicy("CorsPolicy",
+ builder =>
+ {
+ builder.AllowAnyMethod().AllowAnyHeader()
+ .WithOrigins(GlobalContext.SystemConfig.AllowCorsSite.Split(","))
+ .AllowCredentials();
+ }));
+ services.AddOptions();
+ services.AddHttpClient();
+ services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
+ services.AddControllersWithViews().AddControllersAsServices();
+ //启用 Gzip 和 Brotil 压缩功能
+ services.AddResponseCompression(options =>
+ {
+ options.Providers.Add();
+ options.Providers.Add();
+ options.MimeTypes =
+ ResponseCompressionDefaults.MimeTypes.Concat(
+ new[] { "image/svg+xml" });
+ });
+ services.Configure(options =>
+ {
+ options.Level = CompressionLevel.SmallestSize;
+ });
+ services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(GlobalContext.HostingEnvironment.ContentRootPath + Path.DirectorySeparatorChar + "DataProtection"));
+ }
+
+ ///
+ /// Autofac配置
+ ///
+ ///
+ /// 扫描程序
+ /// 控制器
+ /// 服务接口
+ /// 程序
+ public void AutofacConfigureContainer(ContainerBuilder builder, List projects, Type controller, Type IService, Type program)
+ {
+ if (projects == null)
+ {
+ projects = new List();
+ }
+ var baseType = IService;//IDenpendency 是一个接口(所有要实现依赖注入的借口都要继承该接口)
+ var controllerBaseType = controller;
+ foreach (var item in projects)
+ {
+ var assemblys = Assembly.Load(item);//Service是继承接口的实现方法类库名称
+ builder.RegisterAssemblyTypes(assemblys).Where(m => baseType.IsAssignableFrom(m) && m != baseType)
+ .InstancePerLifetimeScope()//生命周期,这里没有使用接口方式
+ .PropertiesAutowired();//属性注入
+ //插件Controller中使用属性注入
+ builder.RegisterAssemblyTypes(assemblys)
+ .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
+ .PropertiesAutowired();
+ }
+ //Controller中使用属性注入
+ builder.RegisterAssemblyTypes(program.Assembly)
+ .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
+ .PropertiesAutowired();
+ //注册html解析
+ builder.RegisterInstance(HtmlEncoder.Create(UnicodeRanges.All)).SingleInstance();
+ }
+
+ public virtual void Configure(IApplicationBuilder app)
+ {
+ GlobalContext.RootServices = app.ApplicationServices;
+ //实时通讯跨域
+ app.UseCors("CorsPolicy");
+ if (WebHostEnvironment.IsDevelopment())
+ {
+ GlobalContext.SystemConfig.Debug = true;
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ //文件地址Resource
+ //静态资源wwwroot
+ app.UseStaticFiles(new StaticFileOptions
+ {
+ ContentTypeProvider = new CustomerFileExtensionContentTypeProvider(),
+ OnPrepareResponse = GlobalContext.SetCacheControl
+ });
+ //启用 Gzip 和 Brotil 压缩功能
+ app.UseResponseCompression();
+ app.Use(async (context, next) =>
+ {
+ context.Request.EnableBuffering();
+ // 执行下一个中间件
+ await next.Invoke();
+ // 释放所有未托管的服务提供器
+ GlobalContext.DisposeUnmanagedObjects();
+ });
+ //session
+ app.UseSession();
+ //路径
+ app.UseRouting();
+ }
+ }
+
+ ///
+ /// StartUp扩展
+ ///
+ public static class StartUpExtends
+ {
+ ///
+ /// 默认MVC配置
+ ///
+ ///
+ ///
+ public static IMvcBuilder AddDefaultMVC(this IServiceCollection services)
+ {
+ return services.AddControllersWithViews(options =>
+ {
+ options.Filters.Add();
+ options.Filters.Add();
+ options.ModelMetadataDetailsProviders.Add(new ModelBindingMetadataProvider());
+ //options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
+ });
+ }
+
+ ///
+ /// 默认API配置
+ ///
+ ///
+ ///
+ public static IMvcBuilder AddDefaultAPI(this IServiceCollection services)
+ {
+ services.AddDirectoryBrowser();
+ return services.AddControllers(options =>
+ {
+ options.Filters.Add();
+ options.ModelMetadataDetailsProviders.Add(new ModelBindingMetadataProvider());
+ }).ConfigureApiBehaviorOptions(options =>
+ {
+ options.SuppressModelStateInvalidFilter = true;
+ });
+ }
+
+ ///
+ /// 默认API配置
+ ///
+ ///
+ ///
+ public static IServiceCollection AddDefaultSwaggerGen(this IServiceCollection services, string name)
+ {
+ services.AddSwaggerGen(config =>
+ {
+ foreach (var item in GlobalContext.SystemConfig.DocumentSettings.GroupOpenApiInfos)
+ {
+ config.SwaggerDoc($"{item.Group}", new OpenApiInfo { Title = item.Title, Version = item.Version, Description = item.Description });
+ }
+ var xmlFile = $"{name}.xml";
+ var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
+ config.IncludeXmlComments(xmlPath, true); //添加控制器层注释(true表示显示控制器注释)
+ config.AddSecurityDefinition(GlobalContext.SystemConfig.TokenName, new OpenApiSecurityScheme
+ {
+ Description = "header token",
+ Name = GlobalContext.SystemConfig.TokenName,
+ In = ParameterLocation.Header,
+ Scheme = "",
+ Type = SecuritySchemeType.ApiKey,//设置类型
+ BearerFormat = ""
+ });
+ config.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = GlobalContext.SystemConfig.TokenName }
+ },
+ new List()
+ }
+ });
+ });
+ return services;
+ }
+
+ ///
+ /// 默认API配置
+ ///
+ ///
+ ///
+ public static IApplicationBuilder AddDefaultSwaggerGen(this IApplicationBuilder app)
+ {
+ app.UseSwagger(c =>
+ {
+ c.RouteTemplate = "api-doc/{documentName}/swagger.json";
+ });
+ app.UseSwaggerUI(c =>
+ {
+ c.RoutePrefix = "api-doc";
+ foreach (var item in GlobalContext.SystemConfig.DocumentSettings.GroupOpenApiInfos)
+ {
+ c.SwaggerEndpoint($"{item.Group}/swagger.json", $"{item.Title}");
+ }
+ });
+ return app;
+ }
+
+ ///
+ /// 注入RabbitMq
+ ///
+ ///
+ /// json配置
+ /// 生命周期,默认:单例模式
+ ///
+ public static IServiceCollection AddRabbitMq(
+ this IServiceCollection @this,
+ ServiceLifetime lifeTime = ServiceLifetime.Singleton)
+ {
+ if (GlobalContext.SystemConfig.RabbitMq.Enabled == false)
+ return @this;
+
+ switch (lifeTime)
+ {
+ case ServiceLifetime.Singleton:
+ @this.AddSingleton(x => new RabbitMqHelper(GlobalContext.SystemConfig.RabbitMq));
+ break;
+
+ case ServiceLifetime.Scoped:
+ @this.AddScoped(x => new RabbitMqHelper(GlobalContext.SystemConfig.RabbitMq));
+ break;
+
+ case ServiceLifetime.Transient:
+ @this.AddTransient(x => new RabbitMqHelper(GlobalContext.SystemConfig.RabbitMq));
+ break;
+
+ default:
+ break;
+ }
+ return @this;
+ }
+
+ #region AddWorkerService
+
+ ///
+ /// 自动注入 继承 BackgroundService 的后台服务
+ ///
+ ///
+ ///
+ public static IServiceCollection AddWorkerService(
+ this IServiceCollection @this)
+ {
+ var ret = new List();
+ var assemblies = Directory.GetFiles(AppContext.BaseDirectory, "WaterCloud.*.dll")
+ .Select(x => x.Substring(@"\").Substring(@"/").Replace(".dll", ""))
+ .Select(x => Assembly.Load(x)).ToArray();
+ //排除列表
+ var ignoreList= new List{ "EventBusHostedService" };
+ foreach (var item in assemblies)
+ {
+ ret.AddRange(item.GetTypes() //获取当前类库下所有类型
+ .Where(t => typeof(BackgroundService).IsAssignableFrom(t)) //获取间接或直接继承t的所有类型
+ .Where(t => !t.IsAbstract && t.IsClass && !ignoreList.Contains(t.Name)));//获取非抽象类 排除接口继承
+ }
+ foreach (var item in ret)
+ {
+ @this.AddTransient(typeof(IHostedService), item);
+ }
+ return @this;
+ }
+
+ #endregion AddWorkerService
+
+ #region AddIf
+
+ ///
+ /// 根据条件注入服务
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddIf(
+ this IServiceCollection @this,
+ bool condition,
+ Action action)
+ {
+ if (condition && action != null)
+ action(@this);
+
+ return @this;
+ }
+
+ #endregion AddIf
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/Enums/SequentialGuidType.cs b/WaterCloud.Code/DistributedIDGenerator/Enums/SequentialGuidType.cs
new file mode 100644
index 0000000..e8d8231
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/Enums/SequentialGuidType.cs
@@ -0,0 +1,40 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System.ComponentModel;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 连续 GUID 类型选项
+ ///
+ public enum SequentialGuidType
+ {
+ ///
+ /// 标准连续 GUID 字符串
+ ///
+ [Description("标准连续 GUID 字符串")]
+ SequentialAsString,
+
+ ///
+ /// Byte 数组类型的连续 `GUID` 字符串
+ ///
+ [Description("Byte 数组类型的连续 `GUID` 字符串")]
+ SequentialAsBinary,
+
+ ///
+ /// 连续部分在末尾展示
+ ///
+ [Description("连续部分在末尾展示")]
+ SequentialAtEnd
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/Generators/IDistributedIDGenerator.cs b/WaterCloud.Code/DistributedIDGenerator/Generators/IDistributedIDGenerator.cs
new file mode 100644
index 0000000..4025cc0
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/Generators/IDistributedIDGenerator.cs
@@ -0,0 +1,27 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 分布式 ID 生成器
+ ///
+ public interface IDistributedIDGenerator
+ {
+ ///
+ /// 生成逻辑
+ ///
+ ///
+ ///
+ object Create(object idGeneratorOptions = default);
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/Generators/SequentialGuidIDGenerator.cs b/WaterCloud.Code/DistributedIDGenerator/Generators/SequentialGuidIDGenerator.cs
new file mode 100644
index 0000000..797313e
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/Generators/SequentialGuidIDGenerator.cs
@@ -0,0 +1,86 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System;
+using System.Security.Cryptography;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 连续 GUID ID 生成器
+ /// 代码参考自:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/blob/ebe011a6f1b2a2a9709fe558cfc7ed3215b55c37/src/EFCore.MySql/ValueGeneration/Internal/MySqlSequentialGuidValueGenerator.cs
+ ///
+ public class SequentialGuidIDGenerator : IDistributedIDGenerator
+ {
+ ///
+ /// 随机数生成器
+ ///
+ private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
+
+ ///
+ /// 生成逻辑
+ ///
+ ///
+ ///
+ public object Create(object idGeneratorOptions = null)
+ {
+ // According to RFC 4122:
+ // dddddddd-dddd-Mddd-Ndrr-rrrrrrrrrrrr
+ // - M = RFC version, in this case '4' for random UUID
+ // - N = RFC variant (plus other bits), in this case 0b1000 for variant 1
+ // - d = nibbles based on UTC date/time in ticks
+ // - r = nibbles based on random bytes
+
+ var options = (idGeneratorOptions ?? new SequentialGuidSettings()) as SequentialGuidSettings;
+
+ var randomBytes = new byte[7];
+ _rng.GetBytes(randomBytes);
+ var ticks = (ulong)options.TimeNow.Ticks;
+
+ var uuidVersion = (ushort)4;
+ var uuidVariant = (ushort)0b1000;
+
+ var ticksAndVersion = (ushort)((ticks << 48 >> 52) | (ushort)(uuidVersion << 12));
+ var ticksAndVariant = (byte)((ticks << 60 >> 60) | (byte)(uuidVariant << 4));
+
+ if (options.LittleEndianBinary16Format)
+ {
+ var guidBytes = new byte[16];
+ var tickBytes = BitConverter.GetBytes(ticks);
+ if (BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(tickBytes);
+ }
+
+ Buffer.BlockCopy(tickBytes, 0, guidBytes, 0, 6);
+ guidBytes[6] = (byte)(ticksAndVersion << 8 >> 8);
+ guidBytes[7] = (byte)(ticksAndVersion >> 8);
+ guidBytes[8] = ticksAndVariant;
+ Buffer.BlockCopy(randomBytes, 0, guidBytes, 9, 7);
+
+ return new Guid(guidBytes);
+ }
+
+ var guid = new Guid((uint)(ticks >> 32), (ushort)(ticks << 32 >> 48), ticksAndVersion,
+ ticksAndVariant,
+ randomBytes[0],
+ randomBytes[1],
+ randomBytes[2],
+ randomBytes[3],
+ randomBytes[4],
+ randomBytes[5],
+ randomBytes[6]);
+
+ return guid;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/IDGen.cs b/WaterCloud.Code/DistributedIDGenerator/IDGen.cs
new file mode 100644
index 0000000..b08142e
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/IDGen.cs
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// ID 生成器
+ ///
+ public static class IDGen
+ {
+ ///
+ /// 生成唯一 ID
+ ///
+ ///
+ ///
+ ///
+ public static object NextID(object idGeneratorOptions)
+ {
+ return ((IDistributedIDGenerator)GlobalContext.RootServices.GetService(typeof(IDistributedIDGenerator))).Create(idGeneratorOptions);
+ }
+
+ ///
+ /// 生成连续 GUID
+ ///
+ ///
+ ///
+ ///
+ public static Guid NextID(SequentialGuidType guidType = SequentialGuidType.SequentialAsString)
+ {
+ var sequentialGuid = GlobalContext.RootServices.GetService(typeof(IDistributedIDGenerator)) as IDistributedIDGenerator;
+ return (Guid)sequentialGuid.Create();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/Settings/SequentialGuidSettings.cs b/WaterCloud.Code/DistributedIDGenerator/Settings/SequentialGuidSettings.cs
new file mode 100644
index 0000000..ad1950d
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/Settings/SequentialGuidSettings.cs
@@ -0,0 +1,32 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 连续 GUID 配置
+ ///
+ public sealed class SequentialGuidSettings
+ {
+ ///
+ /// 当前时间
+ ///
+ public DateTimeOffset TimeNow { get; set; } = DateTimeOffset.UtcNow;
+
+ ///
+ /// LittleEndianBinary 16 格式化
+ ///
+ public bool LittleEndianBinary16Format { get; set; } = false;
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/Constants.cs b/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/Constants.cs
new file mode 100644
index 0000000..6b94011
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/Constants.cs
@@ -0,0 +1,35 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 短 ID 约束
+ ///
+ internal static class Constants
+ {
+ ///
+ /// 最小长度
+ ///
+ public const int MinimumAutoLength = 8;
+
+ ///
+ /// 最大长度
+ ///
+ public const int MaximumAutoLength = 14;
+
+ ///
+ /// 最小可选字符长度
+ ///
+ public const int MinimumCharacterSetLength = 50;
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/RandomHelpers.cs b/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/RandomHelpers.cs
new file mode 100644
index 0000000..6c06ccc
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/ShortID/Internal/RandomHelpers.cs
@@ -0,0 +1,46 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 随机数帮助类
+ ///
+ internal static class RandomHelpers
+ {
+ ///
+ /// 随机数对象
+ ///
+ private static readonly Random Random = new();
+
+ ///
+ /// 线程锁
+ ///
+ private static readonly object ThreadLock = new();
+
+ ///
+ /// 生成线程安全的范围内随机数
+ ///
+ ///
+ ///
+ ///
+ public static int GenerateNumberInRange(int min, int max)
+ {
+ lock (ThreadLock)
+ {
+ return Random.Next(min, max);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/ShortID/Options/GenerationOptions.cs b/WaterCloud.Code/DistributedIDGenerator/ShortID/Options/GenerationOptions.cs
new file mode 100644
index 0000000..73cc5da
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/ShortID/Options/GenerationOptions.cs
@@ -0,0 +1,37 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 短 ID 生成配置选项
+ ///
+ public class GenerationOptions
+ {
+ ///
+ /// 是否使用数字
+ /// 默认 false
+ ///
+ public bool UseNumbers { get; set; }
+
+ ///
+ /// 是否使用特殊字符
+ /// 默认 true
+ ///
+ public bool UseSpecialCharacters { get; set; } = true;
+
+ ///
+ /// 设置短 ID 长度
+ ///
+ public int Length { get; set; } = RandomHelpers.GenerateNumberInRange(Constants.MinimumAutoLength, Constants.MaximumAutoLength);
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/DistributedIDGenerator/ShortID/ShortIDGen.cs b/WaterCloud.Code/DistributedIDGenerator/ShortID/ShortIDGen.cs
new file mode 100644
index 0000000..f8a8c39
--- /dev/null
+++ b/WaterCloud.Code/DistributedIDGenerator/ShortID/ShortIDGen.cs
@@ -0,0 +1,161 @@
+// -----------------------------------------------------------------------------
+// 让 .NET 开发更简单,更通用,更流行。
+// Copyright © 2020-2021 Furion, 百小僧, Baiqian Co.,Ltd.
+//
+// 框架名称:Furion
+// 框架作者:百小僧
+// 框架版本:2.7.9
+// 源码地址:Gitee: https://gitee.com/dotnetchina/Furion
+// Github:https://github.com/monksoul/Furion
+// 开源协议:Apache-2.0(https://gitee.com/dotnetchina/Furion/blob/master/LICENSE)
+// -----------------------------------------------------------------------------
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 短 ID 生成核心代码
+ /// 代码参考自:https://github.com/bolorundurowb/shortid
+ ///
+ public static class ShortIDGen
+ {
+ ///
+ /// 短 ID 生成器期初数据
+ ///
+ private static Random _random = new();
+
+ private const string Bigs = "ABCDEFGHIJKLMNPQRSTUVWXY";
+ private const string Smalls = "abcdefghjklmnopqrstuvwxyz";
+ private const string Numbers = "0123456789";
+ private const string Specials = "_-";
+ private static string _pool = $"{Smalls}{Bigs}";
+
+ ///
+ /// 线程安全锁
+ ///
+ private static readonly object ThreadLock = new();
+
+ ///
+ /// 生成目前比较主流的短 ID
+ /// 包含字母、数字,不包含特殊字符
+ /// 默认生成 8 位
+ ///
+ ///
+ public static string NextID()
+ {
+ return NextID(new GenerationOptions
+ {
+ UseNumbers = true,
+ UseSpecialCharacters = false,
+ Length = 8
+ });
+ }
+
+ ///
+ /// 生成短 ID
+ ///
+ ///
+ ///
+ public static string NextID(GenerationOptions options)
+ {
+ // 配置必填
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ // 判断生成的长度是否小于规定的长度,规定为 8
+ if (options.Length < Constants.MinimumAutoLength)
+ {
+ throw new ArgumentException(
+ $"The specified length of {options.Length} is less than the lower limit of {Constants.MinimumAutoLength} to avoid conflicts.");
+ }
+
+ var characterPool = _pool;
+ var poolBuilder = new StringBuilder(characterPool);
+
+ // 是否包含数字
+ if (options.UseNumbers)
+ {
+ poolBuilder.Append(Numbers);
+ }
+
+ // 是否包含特殊字符
+ if (options.UseSpecialCharacters)
+ {
+ poolBuilder.Append(Specials);
+ }
+
+ var pool = poolBuilder.ToString();
+
+ // 生成拼接
+ var output = new char[options.Length];
+ for (var i = 0; i < options.Length; i++)
+ {
+ lock (ThreadLock)
+ {
+ var charIndex = _random.Next(0, pool.Length);
+ output[i] = pool[charIndex];
+ }
+ }
+
+ return new string(output);
+ }
+
+ ///
+ /// 设置参与运算的字符,最少 50 位
+ ///
+ ///
+ public static void SetCharacters(string characters)
+ {
+ if (string.IsNullOrWhiteSpace(characters))
+ {
+ throw new ArgumentException("The replacement characters must not be null or empty.");
+ }
+
+ var charSet = characters
+ .ToCharArray()
+ .Where(x => !char.IsWhiteSpace(x))
+ .Distinct()
+ .ToArray();
+
+ if (charSet.Length < Constants.MinimumCharacterSetLength)
+ {
+ throw new InvalidOperationException(
+ $"The replacement characters must be at least {Constants.MinimumCharacterSetLength} letters in length and without whitespace.");
+ }
+
+ lock (ThreadLock)
+ {
+ _pool = new string(charSet);
+ }
+ }
+
+ ///
+ /// 设置种子步长
+ ///
+ ///
+ public static void SetSeed(int seed)
+ {
+ lock (ThreadLock)
+ {
+ _random = new Random(seed);
+ }
+ }
+
+ ///
+ /// 重置所有配置
+ ///
+ public static void Reset()
+ {
+ lock (ThreadLock)
+ {
+ _random = new Random();
+ _pool = $"{Smalls}{Bigs}";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Convert.cs b/WaterCloud.Code/Extend/Ext.Convert.cs
new file mode 100644
index 0000000..a7ca62b
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Convert.cs
@@ -0,0 +1,788 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ #region 数值转换
+
+ ///
+ /// 转换为整型
+ ///
+ /// 数据
+ public static int ToInt(this object data)
+ {
+ if (data == null)
+ return 0;
+ int result;
+ var success = int.TryParse(data.ToString(), out result);
+ if (success)
+ return result;
+ try
+ {
+ return Convert.ToInt32(ToDouble(data, 0));
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 将object转换为long,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static long ToLong(this object obj)
+ {
+ try
+ {
+ return long.Parse(obj.ToString());
+ }
+ catch
+ {
+ return 0L;
+ }
+ }
+
+ ///
+ /// 转换为可空整型
+ ///
+ /// 数据
+ public static int? ToIntOrNull(this object data)
+ {
+ if (data == null)
+ return null;
+ int result;
+ bool isValid = int.TryParse(data.ToString(), out result);
+ if (isValid)
+ return result;
+ return null;
+ }
+
+ ///
+ /// 转换为双精度浮点数
+ ///
+ /// 数据
+ public static double ToDouble(this object data)
+ {
+ if (data == null)
+ return 0;
+ double result;
+ return double.TryParse(data.ToString(), out result) ? result : 0;
+ }
+
+ ///
+ /// 转换为双精度浮点数,并按指定的小数位4舍5入
+ ///
+ /// 数据
+ /// 小数位数
+ public static double ToDouble(this object data, int digits)
+ {
+ return Math.Round(ToDouble(data), digits);
+ }
+
+ ///
+ /// 转换为可空双精度浮点数
+ ///
+ /// 数据
+ public static double? ToDoubleOrNull(this object data)
+ {
+ if (data == null)
+ return null;
+ double result;
+ bool isValid = double.TryParse(data.ToString(), out result);
+ if (isValid)
+ return result;
+ return null;
+ }
+
+ ///
+ /// 转换为高精度浮点数
+ ///
+ /// 数据
+ public static decimal ToDecimal(this object data)
+ {
+ if (data == null)
+ return 0;
+ decimal result;
+ return decimal.TryParse(data.ToString(), out result) ? result : 0;
+ }
+
+ ///
+ /// 转换为高精度浮点数,并按指定的小数位4舍5入
+ ///
+ /// 数据
+ /// 小数位数
+ public static decimal ToDecimal(this object data, int digits)
+ {
+ return Math.Round(ToDecimal(data), digits);
+ }
+
+ ///
+ /// 转换为可空高精度浮点数
+ ///
+ /// 数据
+ public static decimal? ToDecimalOrNull(this object data)
+ {
+ if (data == null)
+ return null;
+ decimal result;
+ bool isValid = decimal.TryParse(data.ToString(), out result);
+ if (isValid)
+ return result;
+ return null;
+ }
+
+ ///
+ /// 转换为可空高精度浮点数,并按指定的小数位4舍5入
+ ///
+ /// 数据
+ /// 小数位数
+ public static decimal? ToDecimalOrNull(this object data, int digits)
+ {
+ var result = ToDecimalOrNull(data);
+ if (result == null)
+ return null;
+ return Math.Round(result.Value, digits);
+ }
+
+ #endregion 数值转换
+
+ #region 日期转换
+
+ ///
+ /// 转换为日期
+ ///
+ /// 数据
+ public static DateTime ToDate(this object data)
+ {
+ if (data == null)
+ return DateTime.MinValue;
+ DateTime result;
+ return DateTime.TryParse(data.ToString(), out result) ? result : DateTime.MinValue;
+ }
+
+ ///
+ /// 转换为可空日期
+ ///
+ /// 数据
+ public static DateTime? ToDateOrNull(this object data)
+ {
+ if (data == null)
+ return null;
+ DateTime result;
+ bool isValid = DateTime.TryParse(data.ToString(), out result);
+ if (isValid)
+ return result;
+ return null;
+ }
+
+ #endregion 日期转换
+
+ #region 布尔转换
+
+ ///
+ /// 转换为布尔值
+ ///
+ /// 数据
+ public static bool ToBool(this object data)
+ {
+ if (data == null)
+ return false;
+ bool? value = GetBool(data);
+ if (value != null)
+ return value.Value;
+ bool result;
+ return bool.TryParse(data.ToString(), out result) && result;
+ }
+
+ ///
+ /// 获取布尔值
+ ///
+ private static bool? GetBool(this object data)
+ {
+ switch (data.ToString().Trim().ToLower())
+ {
+ case "0":
+ return false;
+
+ case "1":
+ return true;
+
+ case "是":
+ return true;
+
+ case "否":
+ return false;
+
+ case "yes":
+ return true;
+
+ case "no":
+ return false;
+
+ default:
+ return null;
+ }
+ }
+
+ ///
+ /// 转换为可空布尔值
+ ///
+ /// 数据
+ public static bool? ToBoolOrNull(this object data)
+ {
+ if (data == null)
+ return null;
+ bool? value = GetBool(data);
+ if (value != null)
+ return value.Value;
+ bool result;
+ bool isValid = bool.TryParse(data.ToString(), out result);
+ if (isValid)
+ return result;
+ return null;
+ }
+
+ #endregion 布尔转换
+
+ #region 是否为空
+
+ ///
+ /// 是否为空
+ ///
+ /// 值
+ public static bool IsEmpty(this string value)
+ {
+ return string.IsNullOrWhiteSpace(value);
+ }
+
+ ///
+ /// 是否为空
+ ///
+ ///
+ ///
+ public static bool IsEmpty(this object value)
+ {
+ if (value != null && !string.IsNullOrEmpty(value.ToString()))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// 是否为空
+ ///
+ /// 值
+ public static bool IsEmpty(this Guid? value)
+ {
+ if (value == null)
+ return true;
+ return IsEmpty(value.Value);
+ }
+
+ ///
+ /// 是否为空
+ ///
+ /// 值
+ public static bool IsEmpty(this Guid value)
+ {
+ if (value == Guid.Empty)
+ return true;
+ return false;
+ }
+
+ #endregion 是否为空
+
+ #region 强制转换类型
+
+ ///
+ /// 强制转换类型
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable CastSuper(this IEnumerable source)
+ {
+ foreach (object item in source)
+ {
+ yield return (TResult)Convert.ChangeType(item, typeof(TResult));
+ }
+ }
+
+ #endregion 强制转换类型
+
+ #region 转换为long
+
+ ///
+ /// 将object转换为long,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static long ParseToLong(this object obj)
+ {
+ try
+ {
+ return long.Parse(obj.ToString());
+ }
+ catch
+ {
+ return 0L;
+ }
+ }
+
+ ///
+ /// 将object转换为long,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ ///
+ public static long ParseToLong(this string str, long defaultValue)
+ {
+ try
+ {
+ return long.Parse(str);
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ #endregion 转换为long
+
+ #region 转换为int
+
+ ///
+ /// 将object转换为int,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static int ParseToInt(this object str)
+ {
+ try
+ {
+ return Convert.ToInt32(str);
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 将object转换为int,若转换失败,则返回指定值。不抛出异常。
+ /// null返回默认值
+ ///
+ ///
+ ///
+ ///
+ public static int ParseToInt(this object str, int defaultValue)
+ {
+ if (str == null)
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Convert.ToInt32(str);
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ #endregion 转换为int
+
+ #region 转换为short
+
+ ///
+ /// 将object转换为short,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static short ParseToShort(this object obj)
+ {
+ try
+ {
+ return short.Parse(obj.ToString());
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 将object转换为short,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ public static short ParseToShort(this object str, short defaultValue)
+ {
+ try
+ {
+ return short.Parse(str.ToString());
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ #endregion 转换为short
+
+ #region 转换为demical
+
+ ///
+ /// 将object转换为demical,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ public static decimal ParseToDecimal(this object str, decimal defaultValue)
+ {
+ try
+ {
+ return decimal.Parse(str.ToString());
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ ///
+ /// 将object转换为demical,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static decimal ParseToDecimal(this object str)
+ {
+ try
+ {
+ return decimal.Parse(str.ToString());
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ #endregion 转换为demical
+
+ #region 转化为bool
+
+ ///
+ /// 将object转换为bool,若转换失败,则返回false。不抛出异常。
+ ///
+ ///
+ ///
+ public static bool ParseToBool(this object str)
+ {
+ try
+ {
+ return bool.Parse(str.ToString());
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 将object转换为bool,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ public static bool ParseToBool(this object str, bool result)
+ {
+ try
+ {
+ return bool.Parse(str.ToString());
+ }
+ catch
+ {
+ return result;
+ }
+ }
+
+ #endregion 转化为bool
+
+ #region 转换为float
+
+ ///
+ /// 将object转换为float,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static float ParseToFloat(this object str)
+ {
+ try
+ {
+ return float.Parse(str.ToString());
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 将object转换为float,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ public static float ParseToFloat(this object str, float result)
+ {
+ try
+ {
+ return float.Parse(str.ToString());
+ }
+ catch
+ {
+ return result;
+ }
+ }
+
+ #endregion 转换为float
+
+ #region 转换为Guid
+
+ ///
+ /// 将string转换为Guid,若转换失败,则返回Guid.Empty。不抛出异常。
+ ///
+ ///
+ ///
+ public static Guid ParseToGuid(this string str)
+ {
+ try
+ {
+ return new Guid(str);
+ }
+ catch
+ {
+ return Guid.Empty;
+ }
+ }
+
+ #endregion 转换为Guid
+
+ #region 转换为DateTime
+
+ ///
+ /// 将string转换为DateTime,若转换失败,则返回日期最小值。不抛出异常。
+ ///
+ ///
+ ///
+ public static DateTime ParseToDateTime(this string str)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(str))
+ {
+ return DateTime.MinValue;
+ }
+ if (str.Contains("-") || str.Contains("/"))
+ {
+ return DateTime.Parse(str);
+ }
+ else
+ {
+ int length = str.Length;
+ switch (length)
+ {
+ case 4:
+ return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 6:
+ return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 8:
+ return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 10:
+ return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 12:
+ return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 14:
+ return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+
+ default:
+ return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+ }
+ }
+ }
+ catch
+ {
+ return DateTime.MinValue;
+ }
+ }
+
+ ///
+ /// 将string转换为DateTime,若转换失败,则返回默认值。
+ ///
+ ///
+ ///
+ ///
+ public static DateTime ParseToDateTime(this string str, DateTime? defaultValue)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(str))
+ {
+ return defaultValue.GetValueOrDefault();
+ }
+ if (str.Contains("-") || str.Contains("/"))
+ {
+ return DateTime.Parse(str);
+ }
+ else
+ {
+ int length = str.Length;
+ switch (length)
+ {
+ case 4:
+ return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 6:
+ return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 8:
+ return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 10:
+ return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 12:
+ return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
+
+ case 14:
+ return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+
+ default:
+ return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
+ }
+ }
+ }
+ catch
+ {
+ return defaultValue.GetValueOrDefault();
+ }
+ }
+
+ #endregion 转换为DateTime
+
+ #region 转换为string
+
+ ///
+ /// 将object转换为string,若转换失败,则返回""。不抛出异常。
+ ///
+ ///
+ ///
+ public static string ParseToString(this object obj)
+ {
+ try
+ {
+ if (obj == null)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return obj.ToString();
+ }
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+
+ public static string ParseToStrings(this object obj)
+ {
+ try
+ {
+ var list = obj as IEnumerable;
+ if (list != null)
+ {
+ return string.Join(",", list);
+ }
+ else
+ {
+ return obj.ToString();
+ }
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+
+ #endregion 转换为string
+
+ #region 转换为double
+
+ ///
+ /// 将object转换为double,若转换失败,则返回0。不抛出异常。
+ ///
+ ///
+ ///
+ public static double ParseToDouble(this object obj)
+ {
+ try
+ {
+ return double.Parse(obj.ToString());
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// 将object转换为double,若转换失败,则返回指定值。不抛出异常。
+ ///
+ ///
+ ///
+ ///
+ public static double ParseToDouble(this object str, double defaultValue)
+ {
+ try
+ {
+ return double.Parse(str.ToString());
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ #endregion 转换为double
+
+ ///
+ /// 安全返回值
+ ///
+ /// 可空值
+ public static T SafeValue(this T? value) where T : struct
+ {
+ return value ?? default(T);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.DateTime.cs b/WaterCloud.Code/Extend/Ext.DateTime.cs
new file mode 100644
index 0000000..4d0f494
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.DateTime.cs
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Text;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ ///
+ /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss"
+ ///
+ /// 日期
+ /// 是否移除秒
+ public static string ToDateTimeString(this DateTime dateTime, bool isRemoveSecond = false)
+ {
+ if (isRemoveSecond)
+ return dateTime.ToString("yyyy-MM-dd HH:mm");
+ return dateTime.ToString("yyyy-MM-dd HH:mm:ss");
+ }
+
+ ///
+ /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss"
+ ///
+ /// 日期
+ /// 是否移除秒
+ public static string ToDateTimeString(this DateTime? dateTime, bool isRemoveSecond = false)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToDateTimeString(dateTime.Value, isRemoveSecond);
+ }
+
+ ///
+ /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd"
+ ///
+ /// 日期
+ public static string ToDateString(this DateTime dateTime)
+ {
+ return dateTime.ToString("yyyy-MM-dd");
+ }
+
+ ///
+ /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd"
+ ///
+ /// 日期
+ public static string ToDateString()
+ {
+ return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+ }
+
+ ///
+ /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd"
+ ///
+ /// 日期
+ public static string ToDateString(this DateTime? dateTime)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToDateString(dateTime.Value);
+ }
+
+ ///
+ /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss"
+ ///
+ /// 日期
+ public static string ToTimeString(this DateTime dateTime)
+ {
+ return dateTime.ToString("HH:mm:ss");
+ }
+
+ ///
+ /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss"
+ ///
+ /// 日期
+ public static string ToTimeString(this DateTime? dateTime)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToTimeString(dateTime.Value);
+ }
+
+ ///
+ /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff"
+ ///
+ /// 日期
+ public static string ToMillisecondString(this DateTime dateTime)
+ {
+ return dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
+ }
+
+ ///
+ /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff"
+ ///
+ /// 日期
+ public static string ToMillisecondString(this DateTime? dateTime)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToMillisecondString(dateTime.Value);
+ }
+
+ ///
+ /// 获取格式化字符串,不带时分秒,格式:"yyyy年MM月dd日"
+ ///
+ /// 日期
+ public static string ToChineseDateString(this DateTime dateTime)
+ {
+ return string.Format("{0}年{1}月{2}日", dateTime.Year, dateTime.Month, dateTime.Day);
+ }
+
+ ///
+ /// 获取格式化字符串,不带时分秒,格式:"yyyy年MM月dd日"
+ ///
+ /// 日期
+ public static string ToChineseDateString(this DateTime? dateTime)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToChineseDateString(dateTime.SafeValue());
+ }
+
+ ///
+ /// 获取格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分"
+ ///
+ /// 日期
+ /// 是否移除秒
+ public static string ToChineseDateTimeString(this DateTime dateTime, bool isRemoveSecond = false)
+ {
+ StringBuilder result = new StringBuilder();
+ result.AppendFormat("{0}年{1}月{2}日", dateTime.Year, dateTime.Month, dateTime.Day);
+ result.AppendFormat(" {0}时{1}分", dateTime.Hour, dateTime.Minute);
+ if (isRemoveSecond == false)
+ result.AppendFormat("{0}秒", dateTime.Second);
+ return result.ToString();
+ }
+
+ ///
+ /// 获取格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分"
+ ///
+ /// 日期
+ /// 是否移除秒
+ public static string ToChineseDateTimeString(this DateTime? dateTime, bool isRemoveSecond = false)
+ {
+ if (dateTime == null)
+ return string.Empty;
+ return ToChineseDateTimeString(dateTime.Value);
+ }
+
+ #region 毫秒转天时分秒
+
+ ///
+ /// 毫秒转天时分秒
+ ///
+ ///
+ ///
+ public static string FormatTime(long ms)
+ {
+ int ss = 1000;
+ int mi = ss * 60;
+ int hh = mi * 60;
+ int dd = hh * 24;
+
+ long day = ms / dd;
+ long hour = (ms - day * dd) / hh;
+ long minute = (ms - day * dd - hour * hh) / mi;
+ long second = (ms - day * dd - hour * hh - minute * mi) / ss;
+ long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss;
+
+ string sDay = day < 10 ? "0" + day : "" + day; //天
+ string sHour = hour < 10 ? "0" + hour : "" + hour;//小时
+ string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
+ string sSecond = second < 10 ? "0" + second : "" + second;//秒
+ string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
+ sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
+
+ return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond);
+ }
+
+ #endregion 毫秒转天时分秒
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Enum.cs b/WaterCloud.Code/Extend/Ext.Enum.cs
new file mode 100644
index 0000000..6bafe4b
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Enum.cs
@@ -0,0 +1,114 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ #region 枚举成员转成dictionary类型
+
+ ///
+ /// 转成dictionary类型
+ ///
+ ///
+ ///
+ public static Dictionary EnumToDictionary(this Type enumType)
+ {
+ Dictionary dictionary = new Dictionary();
+ Type typeDescription = typeof(DescriptionAttribute);
+ FieldInfo[] fields = enumType.GetFields();
+ int sValue = 0;
+ string sText = string.Empty;
+ foreach (FieldInfo field in fields)
+ {
+ if (field.FieldType.IsEnum)
+ {
+ sValue = ((int)enumType.InvokeMember(field.Name, BindingFlags.GetField, null, null, null));
+ object[] arr = field.GetCustomAttributes(typeDescription, true);
+ if (arr.Length > 0)
+ {
+ DescriptionAttribute da = (DescriptionAttribute)arr[0];
+ sText = da.Description;
+ }
+ else
+ {
+ sText = field.Name;
+ }
+ dictionary.Add(sValue, sText);
+ }
+ }
+ return dictionary;
+ }
+
+ ///
+ /// 枚举成员转成键值对Json字符串
+ ///
+ ///
+ ///
+ public static string EnumToDictionaryString(this Type enumType)
+ {
+ List> dictionaryList = EnumToDictionary(enumType).ToList();
+ var sJson = JsonConvert.SerializeObject(dictionaryList);
+ return sJson;
+ }
+
+ #endregion 枚举成员转成dictionary类型
+
+ #region 获取枚举的描述
+
+ ///
+ /// 获取枚举值对应的描述
+ ///
+ ///
+ ///
+ public static string GetDescription(this System.Enum enumType)
+ {
+ FieldInfo EnumInfo = enumType.GetType().GetField(enumType.ToString());
+ if (EnumInfo != null)
+ {
+ DescriptionAttribute[] EnumAttributes = (DescriptionAttribute[])EnumInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
+ if (EnumAttributes.Length > 0)
+ {
+ return EnumAttributes[0].Description;
+ }
+ }
+ return enumType.ToString();
+ }
+
+ #endregion 获取枚举的描述
+
+ #region 根据值获取枚举的描述
+
+ public static string GetDescriptionByEnum(this object obj)
+ {
+ var tEnum = System.Enum.Parse(typeof(T), obj.ParseToString()) as System.Enum;
+ var description = tEnum.GetDescription();
+ return description;
+ }
+
+ ///
+ /// 枚举
+ ///
+ ///
+ ///
+ public static string ToDescription(this Enum enumType)
+ {
+ if (enumType == null)
+ return "";
+
+ System.Reflection.FieldInfo fieldInfo = enumType.GetType().GetField(enumType.ToString());
+
+ object[] attribArray = fieldInfo.GetCustomAttributes(false);
+ if (attribArray.Length == 0)
+ return enumType.ToString();
+ else
+ return (attribArray[0] as DescriptionAttribute).Description;
+ }
+
+ #endregion 根据值获取枚举的描述
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Exception.cs b/WaterCloud.Code/Extend/Ext.Exception.cs
new file mode 100644
index 0000000..49daf6f
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Exception.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ public static Exception GetOriginalException(this Exception ex)
+ {
+ if (ex.InnerException == null) return ex;
+
+ return ex.InnerException.GetOriginalException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Format.cs b/WaterCloud.Code/Extend/Ext.Format.cs
new file mode 100644
index 0000000..d8283ae
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Format.cs
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ ///
+ /// 获取描述
+ ///
+ /// 布尔值
+ public static string Description(this bool value)
+ {
+ return value ? "是" : "否";
+ }
+
+ ///
+ /// 获取描述
+ ///
+ /// 布尔值
+ public static string Description(this bool? value)
+ {
+ return value == null ? "" : Description(value.Value);
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this int number, string defaultValue = "")
+ {
+ if (number == 0)
+ return defaultValue;
+ return number.ToString();
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this int? number, string defaultValue = "")
+ {
+ return Format(number.SafeValue(), defaultValue);
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this decimal number, string defaultValue = "")
+ {
+ if (number == 0)
+ return defaultValue;
+ return string.Format("{0:0.##}", number);
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this decimal? number, string defaultValue = "")
+ {
+ return Format(number.SafeValue(), defaultValue);
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this double number, string defaultValue = "")
+ {
+ if (number == 0)
+ return defaultValue;
+ return string.Format("{0:0.##}", number);
+ }
+
+ ///
+ /// 获取格式化字符串
+ ///
+ /// 数值
+ /// 空值显示的默认文本
+ public static string Format(this double? number, string defaultValue = "")
+ {
+ return Format(number.SafeValue(), defaultValue);
+ }
+
+ ///
+ /// 获取格式化字符串,带¥
+ ///
+ /// 数值
+ public static string FormatRmb(this decimal number)
+ {
+ if (number == 0)
+ return "¥0";
+ return string.Format("¥{0:0.##}", number);
+ }
+
+ ///
+ /// 获取格式化字符串,带¥
+ ///
+ /// 数值
+ public static string FormatRmb(this decimal? number)
+ {
+ return FormatRmb(number.SafeValue());
+ }
+
+ ///
+ /// 获取格式化字符串,带%
+ ///
+ /// 数值
+ public static string FormatPercent(this decimal number)
+ {
+ if (number == 0)
+ return string.Empty;
+ return string.Format("{0:0.##}%", number);
+ }
+
+ ///
+ /// 获取格式化字符串,带%
+ ///
+ /// 数值
+ public static string FormatPercent(this decimal? number)
+ {
+ return FormatPercent(number.SafeValue());
+ }
+
+ ///
+ /// 获取格式化字符串,带%
+ ///
+ /// 数值
+ public static string FormatPercent(this double number)
+ {
+ if (number == 0)
+ return string.Empty;
+ return string.Format("{0:0.##}%", number);
+ }
+
+ ///
+ /// 获取格式化字符串,带%
+ ///
+ /// 数值
+ public static string FormatPercent(this double? number)
+ {
+ return FormatPercent(number.SafeValue());
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Linq.cs b/WaterCloud.Code/Extend/Ext.Linq.cs
new file mode 100644
index 0000000..74a74fd
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Linq.cs
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace WaterCloud.Code
+{
+ public static partial class ExtLinq
+ {
+ public static Expression Property(this Expression expression, string propertyName)
+ {
+ return Expression.Property(expression, propertyName);
+ }
+
+ public static Expression AndAlso(this Expression left, Expression right)
+ {
+ return Expression.AndAlso(left, right);
+ }
+
+ public static Expression OrElse(this Expression left, Expression right)
+ {
+ return Expression.OrElse(left, right);
+ }
+
+ public static Expression Call(this Expression instance, string methodName, params Expression[] arguments)
+ {
+ return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments);
+ }
+
+ public static Expression GreaterThan(this Expression left, Expression right)
+ {
+ return Expression.GreaterThan(left, right);
+ }
+
+ public static Expression ToLambda(this Expression body, params ParameterExpression[] parameters)
+ {
+ return Expression.Lambda(body, parameters);
+ }
+
+ public static Expression> True()
+ { return param => true; }
+
+ public static Expression> False()
+ { return param => false; }
+
+ public static Expression> AndAlso(this Expression> first, Expression> second)
+ {
+ return first.Compose(second, Expression.AndAlso);
+ }
+
+ public static Expression> OrElse(this Expression> first, Expression> second)
+ {
+ return first.Compose(second, Expression.OrElse);
+ }
+
+ public static Expression Compose(this Expression first, Expression second, Func merge)
+ {
+ var map = first.Parameters
+ .Select((f, i) => new { f, s = second.Parameters[i] })
+ .ToDictionary(p => p.s, p => p.f);
+ var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
+ return Expression.Lambda(merge(first.Body, secondBody), first.Parameters);
+ }
+
+ private class ParameterRebinder : ExpressionVisitor
+ {
+ private readonly Dictionary map;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The map.
+ private ParameterRebinder(Dictionary map)
+ {
+ this.map = map ?? new Dictionary();
+ }
+
+ ///
+ /// Replaces the parameters.
+ ///
+ /// The map.
+ /// The exp.
+ /// Expression
+ public static Expression ReplaceParameters(Dictionary map, Expression exp)
+ {
+ return new ParameterRebinder(map).Visit(exp);
+ }
+
+ protected override Expression VisitParameter(ParameterExpression p)
+ {
+ ParameterExpression replacement;
+
+ if (map.TryGetValue(p, out replacement))
+ {
+ p = replacement;
+ }
+ return base.VisitParameter(p);
+ }
+ }
+
+ public static ParameterExpression CreateLambdaParam(string name)
+ {
+ return Expression.Parameter(typeof(T), name);
+ }
+
+ ///
+ /// 创建完整的lambda
+ ///
+ public static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body)
+ {
+ //c=>c.XXX=="XXX"
+ return Expression.Lambda(body, param);
+ }
+
+ public static Expression> GenerateTypeLambda(this ParameterExpression param, Expression body)
+ {
+ return (Expression>)(param.GenerateLambda(body));
+ }
+
+ public static Expression Or(this Expression expression, Expression expressionRight)
+ {
+ return Expression.Or(expression, expressionRight);
+ }
+
+ public static Expression And(this Expression expression, Expression expressionRight)
+ {
+ return Expression.And(expression, expressionRight);
+ }
+
+ public static IOrderedQueryable SortBy(this IQueryable query, Expression> sortPredicate)
+ where TEntity : class, new()
+ {
+ return InvokeSortBy(query, sortPredicate, SortOrder.Ascending);
+ }
+
+ public static IOrderedQueryable SortByDescending(this IQueryable query, Expression> sortPredicate)
+ where TEntity : class, new()
+ {
+ return InvokeSortBy(query, sortPredicate, SortOrder.Descending);
+ }
+
+ private static IOrderedQueryable InvokeSortBy(IQueryable query,
+ Expression> sortPredicate, SortOrder sortOrder)
+ where TEntity : class, new()
+ {
+ var param = sortPredicate.Parameters[0];
+ string propertyName = null;
+ Type propertyType = null;
+ Expression bodyExpression = null;
+ if (sortPredicate.Body is UnaryExpression)
+ {
+ var unaryExpression = sortPredicate.Body as UnaryExpression;
+ bodyExpression = unaryExpression.Operand;
+ }
+ else if (sortPredicate.Body is MemberExpression)
+ {
+ bodyExpression = sortPredicate.Body;
+ }
+ else
+ throw new ArgumentException(@"The body of the sort predicate expression should be
+ either UnaryExpression or MemberExpression.", "sortPredicate");
+ var memberExpression = (MemberExpression)bodyExpression;
+ propertyName = memberExpression.Member.Name;
+ if (memberExpression.Member.MemberType == MemberTypes.Property)
+ {
+ var propertyInfo = memberExpression.Member as PropertyInfo;
+ if (propertyInfo != null) propertyType = propertyInfo.PropertyType;
+ }
+ else
+ throw new InvalidOperationException(@"Cannot evaluate the type of property since the member expression
+ represented by the sort predicate expression does not contain a PropertyInfo object.");
+
+ var funcType = typeof(Func<,>).MakeGenericType(typeof(TEntity), propertyType);
+ var convertedExpression = Expression.Lambda(funcType,
+ Expression.Convert(Expression.Property(param, propertyName), propertyType), param);
+
+ var sortingMethods = typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static);
+ var sortingMethodName = GetSortingMethodName(sortOrder);
+ var sortingMethod = sortingMethods.First(sm => sm.Name == sortingMethodName &&
+ sm.GetParameters().Length == 2);
+ return (IOrderedQueryable)sortingMethod
+ .MakeGenericMethod(typeof(TEntity), propertyType)
+ .Invoke(null, new object[] { query, convertedExpression });
+ }
+
+ private static string GetSortingMethodName(SortOrder sortOrder)
+ {
+ switch (sortOrder)
+ {
+ case SortOrder.Ascending:
+ return "OrderBy";
+
+ case SortOrder.Descending:
+ return "OrderByDescending";
+
+ default:
+ throw new ArgumentException("Sort Order must be specified as either Ascending or Descending.",
+ "sortOrder");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.List.cs b/WaterCloud.Code/Extend/Ext.List.cs
new file mode 100644
index 0000000..ef61c00
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.List.cs
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ ///
+ /// 获取表里某页的数据
+ ///
+ /// 表数据
+ /// 当前页
+ /// 分页大小
+ /// 返回总页数
+ /// 返回当页表数据
+ public static List GetPage(this List data, int pageIndex, int pageSize, out int allPage)
+ {
+ allPage = 1;
+ return null;
+ }
+
+ ///
+ /// IList转成List
+ ///
+ ///
+ ///
+ ///
+ public static List IListToList(IList list)
+ {
+ T[] array = new T[list.Count];
+ list.CopyTo(array, 0);
+ return new List(array);
+ }
+
+ ///
+ /// 去除空元素
+ ///
+ public static List removeNull(List oldList)
+ {
+ // 临时集合
+ List listTemp = new List();
+ foreach (var item in oldList)
+ {
+ if (!string.IsNullOrEmpty(item))
+ {
+ listTemp.Add(item);
+ }
+ }
+ return listTemp;
+ }
+ }
+
+ public class ExtList : IEqualityComparer where T : class, new()
+ {
+ private string[] comparintFiledName = { };
+
+ public ExtList()
+ { }
+
+ public ExtList(params string[] comparintFiledName)
+ {
+ this.comparintFiledName = comparintFiledName;
+ }
+
+ bool IEqualityComparer.Equals(T x, T y)
+ {
+ if (x == null && y == null)
+ {
+ return false;
+ }
+ if (comparintFiledName.Length == 0)
+ {
+ return x.Equals(y);
+ }
+ bool result = true;
+ var typeX = x.GetType();//获取类型
+ var typeY = y.GetType();
+ foreach (var filedName in comparintFiledName)
+ {
+ var xPropertyInfo = (from p in typeX.GetProperties() where p.Name.Equals(filedName) select p).FirstOrDefault();
+ var yPropertyInfo = (from p in typeY.GetProperties() where p.Name.Equals(filedName) select p).FirstOrDefault();
+
+ result = result
+ && xPropertyInfo != null && yPropertyInfo != null
+ && xPropertyInfo.GetValue(x, null).ToString().Equals(yPropertyInfo.GetValue(y, null));
+ }
+ return result;
+ }
+
+ int IEqualityComparer.GetHashCode(T obj)
+ {
+ return obj.ToString().GetHashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Mapper.cs b/WaterCloud.Code/Extend/Ext.Mapper.cs
new file mode 100644
index 0000000..0b1a095
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Mapper.cs
@@ -0,0 +1,59 @@
+using AutoMapper;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ ///
+ /// 类型映射
+ ///
+ public static T MapTo(this object obj)
+ {
+ if (obj == null) return default(T);
+
+ var config = new MapperConfiguration(cfg => cfg.CreateMap(obj.GetType(), typeof(T)));
+ var mapper = config.CreateMapper();
+ return mapper.Map(obj);
+ }
+
+ ///
+ /// 集合列表类型映射
+ ///
+ public static List MapToList(this IEnumerable source)
+ {
+ Type sourceType = source.GetType().GetGenericArguments()[0]; //获取枚举的成员类型
+ var config = new MapperConfiguration(cfg => cfg.CreateMap(sourceType, typeof(TDestination)));
+ var mapper = config.CreateMapper();
+
+ return mapper.Map>(source);
+ }
+
+ ///
+ /// 集合列表类型映射
+ ///
+ public static List MapToList(this IEnumerable source)
+ {
+ var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination)));
+ var mapper = config.CreateMapper();
+
+ return mapper.Map>(source);
+ }
+
+ ///
+ /// 类型映射
+ ///
+ public static TDestination MapTo(this TSource source, TDestination destination)
+ where TSource : class
+ where TDestination : class
+ {
+ if (source == null) return destination;
+
+ var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination)));
+ var mapper = config.CreateMapper();
+ return mapper.Map(source);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Number.cs b/WaterCloud.Code/Extend/Ext.Number.cs
new file mode 100644
index 0000000..d8b3c41
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Number.cs
@@ -0,0 +1,96 @@
+
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 数值扩展类
+ ///
+ public static partial class Extensions
+ {
+ #region 进制转换
+ ///
+ /// 10进制转换到2-36进制
+ ///
+ /// 10进制数字
+ /// 进制,范围2-36
+ /// 编码取值规则,最大转换位数不能大于该字符串的长度
+ ///
+ public static string ToBase(this long @this, int radix, string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ {
+ const int BitsInLong = 64;
+
+ if (radix < 2 || radix > digits.Length)
+ throw new ArgumentException("The radix must be >= 2 and <= " + digits.Length.ToString());
+
+ if (@this == 0)
+ return "0";
+
+ var index = BitsInLong - 1;
+ var currentNumber = Math.Abs(@this);
+ var charArray = new char[BitsInLong];
+
+ while (currentNumber != 0)
+ {
+ var remainder = (int)(currentNumber % radix);
+ charArray[index--] = digits[remainder];
+ currentNumber /= radix;
+ }
+
+ var result = new string(charArray, index + 1, BitsInLong - index - 1);
+ if (@this < 0)
+ {
+ result = "-" + result;
+ }
+
+ return result;
+ }
+
+ ///
+ /// byte转16进制
+ ///
+ ///
+ ///
+ public static string ToHex(this byte @this) => Convert.ToString(@this, 16);
+
+ ///
+ /// 2进制转16进制
+ ///
+ ///
+ ///
+ public static string ToHex(this string @this) => Convert.ToString(Convert.ToInt64(@this, 2), 16);
+
+ ///
+ /// 16进制转2进制
+ ///
+ ///
+ ///
+ public static string ToBinary(this string @this) => Convert.ToString(Convert.ToInt64(@this, 16), 2);
+
+ ///
+ /// 2进制/16进制转8进制
+ ///
+ ///
+ /// 2或者16,表示2进制或者16进制;
+ ///
+ public static string ToOctal(this string @this, int fromBase) => Convert.ToString(Convert.ToInt64(@this, fromBase), 8);
+
+ ///
+ /// 2进制/16进制转10进制
+ ///
+ ///
+ /// 2或者16,表示2进制或者16进制;
+ ///
+ public static string ToDecimalism(this string @this, int fromBase)
+ {
+ if (fromBase == 16)
+ return Convert.ToInt32(@this, 16).ToString();
+ else
+ return Convert.ToString(Convert.ToInt64(@this, 2), 10);
+ }
+ #endregion
+ }
+}
diff --git a/WaterCloud.Code/Extend/Ext.String.cs b/WaterCloud.Code/Extend/Ext.String.cs
new file mode 100644
index 0000000..fb08a10
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.String.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// string扩展类
+ ///
+ public static partial class Extensions
+ {
+ ///
+ /// 从分隔符开始向尾部截取字符串
+ ///
+ /// 源字符串
+ /// 分隔符
+ /// true:从最后一个匹配的分隔符开始截取,false:从第一个匹配的分隔符开始截取,默认:true
+ /// string
+ public static string Substring(this string @this, string separator, bool lastIndexOf = true)
+ {
+ var startIndex = (lastIndexOf ?
+ @this.LastIndexOf(separator, StringComparison.OrdinalIgnoreCase) :
+ @this.IndexOf(separator, StringComparison.OrdinalIgnoreCase)) +
+ separator.Length;
+
+ var length = @this.Length - startIndex;
+ return @this.Substring(startIndex, length);
+ }
+ #region 字符串截取第一个
+ public static string ReplaceFrist(this string str, string oldChar, string newChar)
+ {
+ int idx = str.IndexOf(oldChar);
+ str = str.Remove(idx, oldChar.Length);
+ str = str.Insert(idx, newChar);
+ return str;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Table.cs b/WaterCloud.Code/Extend/Ext.Table.cs
new file mode 100644
index 0000000..b6e0361
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Table.cs
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System.Data;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ ///
+ /// 获取表里某页的数据
+ ///
+ /// 表数据
+ /// 当前页
+ /// 分页大小
+ /// 返回总页数
+ /// 返回当页表数据
+ public static DataTable GetPage(this DataTable data, int pageIndex, int pageSize, out int allPage)
+ {
+ allPage = data.Rows.Count / pageSize;
+ allPage += data.Rows.Count % pageSize == 0 ? 0 : 1;
+ DataTable Ntable = data.Clone();
+ int startIndex = pageIndex * pageSize;
+ int endIndex = startIndex + pageSize > data.Rows.Count ? data.Rows.Count : startIndex + pageSize;
+ if (startIndex < endIndex)
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ Ntable.ImportRow(data.Rows[i]);
+ }
+ return Ntable;
+ }
+
+ ///
+ /// 根据字段过滤表的内容
+ ///
+ /// 表数据
+ /// 条件
+ ///
+ ///
+ public static DataTable GetDataFilter(DataTable data, string condition)
+ {
+ if (data != null && data.Rows.Count > 0)
+ {
+ if (condition.Trim() == "")
+ {
+ return data;
+ }
+ else
+ {
+ DataTable newdt = new DataTable();
+ newdt = data.Clone();
+ DataRow[] dr = data.Select(condition);
+ for (int i = 0; i < dr.Length; i++)
+ {
+ newdt.ImportRow((DataRow)dr[i]);
+ }
+ return newdt;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Extend/Ext.Validate.cs b/WaterCloud.Code/Extend/Ext.Validate.cs
new file mode 100644
index 0000000..7f77108
--- /dev/null
+++ b/WaterCloud.Code/Extend/Ext.Validate.cs
@@ -0,0 +1,30 @@
+using Microsoft.AspNetCore.Http;
+using System;
+
+namespace WaterCloud.Code
+{
+ public static partial class Extensions
+ {
+ public static bool IsNullOrZero(this object value)
+ {
+ if (value == null || value.ParseToString().Trim() == "0")
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public static bool IsAjaxRequest(this HttpRequest request)
+ {
+ if (request == null)
+ throw new ArgumentNullException("request");
+
+ if (request.Headers != null)
+ return request.Headers["X-Requested-With"] == "XMLHttpRequest";
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Filter/GlobalExceptionFilter.cs b/WaterCloud.Code/Filter/GlobalExceptionFilter.cs
new file mode 100644
index 0000000..8347bd0
--- /dev/null
+++ b/WaterCloud.Code/Filter/GlobalExceptionFilter.cs
@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 全局异常过滤器,MVC使用
+ ///
+ public class GlobalExceptionFilter : IExceptionFilter, IAsyncExceptionFilter
+ {
+ public void OnException(ExceptionContext context)
+ {
+ LogHelper.WriteWithTime(context);
+ if (context.HttpContext.Request.IsAjaxRequest())
+ {
+ AlwaysResult obj = new AlwaysResult();
+ obj.state = ResultType.error.ToString();
+ obj.message = context.Exception.GetOriginalException().Message;
+ if (string.IsNullOrEmpty(obj.message))
+ {
+ obj.message = "抱歉,系统错误,请联系管理员!";
+ }
+ context.Result = new JsonResult(obj);
+ context.ExceptionHandled = true;
+ }
+ else
+ {
+ //context.HttpContext.Response.WriteAsync("");
+ context.Result = new RedirectResult(context.HttpContext.Request.PathBase + "/Home/Error?msg=500");
+ context.ExceptionHandled = true;
+ }
+ }
+
+ public Task OnExceptionAsync(ExceptionContext context)
+ {
+ OnException(context);
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Filter/GlobalExceptionMiddleware.cs b/WaterCloud.Code/Filter/GlobalExceptionMiddleware.cs
new file mode 100644
index 0000000..d8c730b
--- /dev/null
+++ b/WaterCloud.Code/Filter/GlobalExceptionMiddleware.cs
@@ -0,0 +1,47 @@
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using System;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 全局异常中间件,api使用
+ ///
+ public class GlobalExceptionMiddleware
+ {
+ private readonly RequestDelegate next;
+
+ public GlobalExceptionMiddleware(RequestDelegate next)
+ {
+ this.next = next;
+ }
+
+ public async Task Invoke(HttpContext context /* other dependencies */)
+ {
+ try
+ {
+ await next(context);
+ }
+ catch (Exception ex)
+ {
+ await HandleExceptionAsync(context, ex);
+ }
+ }
+
+ private static Task HandleExceptionAsync(HttpContext context, Exception exception)
+ {
+ var code = HttpStatusCode.OK;
+ LogHelper.WriteWithTime(exception);
+ var result = JsonConvert.SerializeObject(new AlwaysResult
+ {
+ state = ResultType.error.ToString(),
+ message = exception.Message
+ });
+ context.Response.ContentType = "application/json";
+ context.Response.StatusCode = (int)code;
+ return context.Response.WriteAsync(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Filter/ModelActionFilter.cs b/WaterCloud.Code/Filter/ModelActionFilter.cs
new file mode 100644
index 0000000..17ac71f
--- /dev/null
+++ b/WaterCloud.Code/Filter/ModelActionFilter.cs
@@ -0,0 +1,31 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 模型验证过滤器
+ ///
+ public class ModelActionFilter : ActionFilterAttribute, IActionFilter
+ {
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ string message = "";
+ if (!context.ModelState.IsValid)
+ {
+ foreach (var item in context.ModelState.Values)
+ {
+ foreach (var error in item.Errors)
+ {
+ message = message += error.ErrorMessage + "|";
+ }
+ }
+ if (message.Length > 0)
+ {
+ message = message.Substring(0, message.Length - 1);
+ }
+ context.Result = new JsonResult(new AlwaysResult { state = ResultType.error.ToString(), message = message });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Flow/Flow.cs b/WaterCloud.Code/Flow/Flow.cs
new file mode 100644
index 0000000..84e3a20
--- /dev/null
+++ b/WaterCloud.Code/Flow/Flow.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ public class Flow
+ {
+ public string title { get; set; }
+ public int initNum { get; set; }
+ public List lines { get; set; }
+ public List nodes { get; set; }
+ public List areas { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Flow/FlowArea.cs b/WaterCloud.Code/Flow/FlowArea.cs
new file mode 100644
index 0000000..a777d27
--- /dev/null
+++ b/WaterCloud.Code/Flow/FlowArea.cs
@@ -0,0 +1,18 @@
+namespace WaterCloud.Code
+{
+ public class FlowArea
+ {
+ public string id { get; set; }
+
+ public string name { get; set; }
+
+ public string color { get; set; }
+
+ public int left { get; set; }
+ public int top { get; set; }
+
+ public int width { get; set; }
+ public int height { get; set; }
+ public bool alt { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Flow/FlowLine.cs b/WaterCloud.Code/Flow/FlowLine.cs
new file mode 100644
index 0000000..f506150
--- /dev/null
+++ b/WaterCloud.Code/Flow/FlowLine.cs
@@ -0,0 +1,272 @@
+using Newtonsoft.Json.Linq;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 流程连线
+ ///
+ public class FlowLine
+ {
+ public string id { get; set; }
+ public string label { get; set; }
+ public string type { get; set; }
+ public string from { get; set; }
+ public string to { get; set; }
+ public string name { get; set; }
+ public bool dash { get; set; }
+ public double M { get; set; }
+ public bool alt { get; set; }
+
+ /// 分支条件
+ public List Compares { get; set; }
+
+ public bool Compare(JObject frmDataJson)
+ {
+ bool result = true;
+ foreach (var compare in Compares)
+ {
+ compare.FieldName = compare.FieldName.ToLower();
+ compare.Value = compare.Value.ToLower();
+ decimal value = 0; //参考值
+ decimal frmvalue = 0; //表单中填写的值
+ if (compare.Operation != DataCompare.Equal && compare.Operation != DataCompare.NotEqual)
+ {
+ value = decimal.Parse(compare.Value);
+ frmvalue = decimal.Parse(frmDataJson.GetValue(compare.FieldName.ToLower()).ToString()); //表单中填写的值
+ }
+ bool res = false;
+ if (compare.Condition == "and")
+ {
+ switch (compare.Operation)
+ {
+ case DataCompare.Equal:
+ result &= compare.Value == frmDataJson.GetValue(compare.FieldName).ToString();
+ break;
+
+ case DataCompare.NotEqual:
+ result &= compare.Value != frmDataJson.GetValue(compare.FieldName).ToString();
+ break;
+
+ case DataCompare.Larger:
+ result &= frmvalue > value;
+ break;
+
+ case DataCompare.Less:
+ result &= frmvalue < value;
+ break;
+
+ case DataCompare.LargerEqual:
+ result &= frmvalue <= value;
+ break;
+
+ case DataCompare.LessEqual:
+ result &= frmvalue <= value;
+ break;
+
+ case DataCompare.In:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = true;
+ break;
+ }
+ }
+ result &= res;
+ break;
+ }
+ else
+ {
+ var arr = compare.Value.Split(',');
+ if (arr.Contains(frmvalue.ToString()))
+ {
+ res = true;
+ break;
+ }
+ result &= res;
+ break;
+ }
+ case DataCompare.NotIn:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = false;
+ break;
+ }
+ }
+ result &= res;
+ break;
+ }
+ else
+ {
+ var arr = compare.Value.Split(',');
+ if (arr.Contains(frmvalue.ToString()))
+ {
+ res = false;
+ break;
+ }
+ result &= res;
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (compare.Operation)
+ {
+ case DataCompare.Equal:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = true;
+ break;
+ }
+ }
+ result |= res;
+ break;
+ }
+ result |= compare.Value == frmDataJson.GetValue(compare.FieldName).ToString();
+ break;
+
+ case DataCompare.NotEqual:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = false;
+ break;
+ }
+ }
+ result |= res;
+ break;
+ }
+ result |= compare.Value != frmDataJson.GetValue(compare.FieldName).ToString();
+ break;
+
+ case DataCompare.Larger:
+ result |= frmvalue > value;
+ break;
+
+ case DataCompare.Less:
+ result |= frmvalue < value;
+ break;
+
+ case DataCompare.LargerEqual:
+ result |= frmvalue <= value;
+ break;
+
+ case DataCompare.LessEqual:
+ result |= frmvalue <= value;
+ break;
+
+ case DataCompare.In:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = true;
+ break;
+ }
+ }
+ result |= res;
+ break;
+ }
+ else
+ {
+ var arr = compare.Value.Split(',');
+ if (arr.Contains(frmvalue.ToString()))
+ {
+ res = true;
+ }
+ result |= res;
+ break;
+ }
+ case DataCompare.NotIn:
+ if (compare.FieldName == "申请人" || compare.FieldName == "所属部门")
+ {
+ var arr = compare.Value.Split(',');
+ foreach (var item in frmDataJson.GetValue(compare.FieldName).ToString().Split(','))
+ {
+ if (arr.Contains(item))
+ {
+ res = false;
+ break;
+ }
+ }
+ result |= res;
+ break;
+ }
+ else
+ {
+ var arr = compare.Value.Split(',');
+ if (arr.Contains(frmvalue.ToString()))
+ {
+ res = false;
+ }
+ result |= res;
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+
+ ///
+ /// 分支条件
+ ///
+ public class DataCompare
+ {
+ public const string Larger = ">";
+ public const string Less = "<";
+ public const string LargerEqual = ">=";
+ public const string LessEqual = "<=";
+ public const string NotEqual = "!=";
+ public const string Equal = "=";
+ public const string In = "in";
+ public const string NotIn = "not in";
+
+ /// 操作类型比如大于/等于/小于
+ public string Operation { get; set; }
+
+ /// form种的字段名称
+ public string FieldName { get; set; }
+
+ /// 字段类型:"form":为表单中的字段,后期扩展系统表等.
+ public string FieldType { get; set; }
+
+ /// 实际的值
+ public string Value { get; set; }
+
+ ///
+ /// 显示值
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 条件关系
+ ///
+ public string Condition { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Flow/FlowNode.cs b/WaterCloud.Code/Flow/FlowNode.cs
new file mode 100644
index 0000000..8c43674
--- /dev/null
+++ b/WaterCloud.Code/Flow/FlowNode.cs
@@ -0,0 +1,131 @@
+namespace WaterCloud.Code
+{
+ ///
+ /// 流程节点
+ ///
+ public class FlowNode
+ {
+ public const string START = "start round mix";
+ public const string END = "end round";
+ public const string NODE = "node";
+ public const string FORK = "fork"; //会签开始节点
+ public const string JOIN = "join"; //会签结束节点
+
+ public string id { get; set; }
+
+ public string name { get; set; }
+
+ public string type { get; set; }
+
+ public int left { get; set; }
+ public int top { get; set; }
+
+ public int width { get; set; }
+ public int height { get; set; }
+ public bool alt { get; set; }
+
+ ///
+ /// 节点的附加数据项
+ ///
+ /// The set information.
+ public Setinfo setInfo { get; set; }
+ }
+
+ public class Setinfo
+ {
+ public const string SPECIAL_USER = "SPECIAL_USER"; //指定用户
+ public const string ALL_USER = "ALL_USER"; //所有用户
+ public const string SPECIAL_ROLE = "SPECIAL_ROLE"; //指定角色
+ public const string DEPARTMENT_MANAGER = "DEPARTMENT_MANAGER"; //部门负责人
+ public const string USER_MANAGER = "USER_MANAGER"; //直属上级
+ public const string MORE_USER_MANAGER = "MORE_USER_MANAGER"; //连续多级直属上级
+ public const string RUNTIME_SPECIAL_ROLE = "RUNTIME_SPECIAL_ROLE"; //运行时指定角色
+ public const string RUNTIME_SPECIAL_USER = "RUNTIME_SPECIAL_USER"; //运行时指定用户
+
+ ///
+ /// 节点执行权限类型
+ ///
+ public string NodeDesignate { get; set; }
+
+ public Nodedesignatedata NodeDesignateData { get; set; }
+ public string NodeCode { get; set; }
+ public string NodeName { get; set; }
+
+ ///
+ /// 流程执行时,三方回调的URL地址
+ ///
+ public string ThirdPartyUrl { get; set; }
+
+ ///
+ /// 驳回节点0"前一步"1"第一步"2"某一步" 3"不处理"
+ ///
+ public string NodeRejectType { get; set; }
+
+ public int? Taged { get; set; }
+ public string UserName { get; set; }
+ public string UserId { get; set; }
+ public string Description { get; set; }
+ public string TagedTime { get; set; }
+
+ //节点会签方式,
+ //all/空:默认为全部通过
+ //one :至少有一个通过
+ public string NodeConfluenceType { get; set; }
+
+ ///
+ /// 会签通过的个数
+ ///
+ public int? ConfluenceOk { get; set; }
+
+ ///
+ /// 会签拒绝的个数
+ ///
+ public int? ConfluenceNo { get; set; }
+
+ ///
+ /// 可写的表单项ID
+ ///
+ public string[] CanWriteFormItemIds { get; set; }
+ }
+
+ ///
+ /// 节点执行人
+ ///
+ public class Nodedesignatedata
+ {
+ public string[] users { get; set; }
+ public string[] roles { get; set; }
+ public string[] orgs { get; set; }
+ public bool currentDepart { get; set; }
+ }
+
+ ///
+ /// 节点执行结果标签
+ ///
+ public class Tag
+ {
+ ///
+ /// 1: 通过
+ /// 2:不通过
+ /// 3:驳回
+ ///
+ public int Taged { get; set; }
+
+ public string UserId { get; set; }
+ public string UserName { get; set; }
+ public string Description { get; set; }
+ public string TagedTime { get; set; }
+ }
+
+ ///
+ /// 1: 通过
+ /// 2:不通过
+ /// 3:驳回
+ ///
+ public enum TagState
+ {
+ Ok = 1,
+ No,
+ Reject
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Form/FormUtil.cs b/WaterCloud.Code/Form/FormUtil.cs
new file mode 100644
index 0000000..863795d
--- /dev/null
+++ b/WaterCloud.Code/Form/FormUtil.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace WaterCloud.Code
+{
+ public class FormUtil
+ {
+ ///
+ /// 获取值
+ ///
+ /// The form.
+ /// System.String.
+ public static List SetValue(string content)
+ {
+ List list = JsonHelper.ToObject>(content);
+ List temp = new List();
+ SetFormValue(list, temp);
+ return temp;
+ }
+
+ private static List SetFormValue(List list, List temp)
+ {
+ foreach (var item in list)
+ {
+ if (item.tag == "grid")
+ {
+ foreach (var column in item.columns)
+ {
+ SetFormValue(column.list, temp);
+ }
+ }
+ else
+ {
+ temp.Add(item.id);
+ }
+ }
+ return temp;
+ }
+
+ public static List SetValueByWeb(string webForm)
+ {
+ var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
+ var referencedAssemblies = Directory.GetFiles(path, "*.dll").Select(Assembly.LoadFrom).ToArray();
+ var t = referencedAssemblies
+ .SelectMany(a => a.GetTypes().Where(t => t.FullName.Contains("WaterCloud.Domain.") && t.FullName.Contains("." + webForm + "Entity"))).First();
+ List temp = new List();
+ PropertyInfo[] pArray = t.GetProperties();
+ Array.ForEach(pArray, p =>
+ {
+ temp.Add(p.Name);
+ });
+ return temp;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Form/FormValue.cs b/WaterCloud.Code/Form/FormValue.cs
new file mode 100644
index 0000000..edf3e12
--- /dev/null
+++ b/WaterCloud.Code/Form/FormValue.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 表单设计类
+ ///
+ public class FormValue
+ {
+ public string id { get; set; }
+ public string label { get; set; }
+ public int index { get; set; }
+ public string tag { get; set; }
+ public int span { get; set; }
+ public List columns { get; set; }
+ public string name { get; set; }
+ }
+
+ public class FormEx
+ {
+ public int span { get; set; }
+ public List list { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Globals/GlobalContext.cs b/WaterCloud.Code/Globals/GlobalContext.cs
new file mode 100644
index 0000000..4ab9d24
--- /dev/null
+++ b/WaterCloud.Code/Globals/GlobalContext.cs
@@ -0,0 +1,254 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.StaticFiles;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyModel;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
+using System.Text;
+using WaterCloud.Code.Model;
+
+namespace WaterCloud.Code
+{
+ public static class GlobalContext
+ {
+
+ ///
+ /// 构造函数
+ ///
+ static GlobalContext()
+ {
+ // 未托管的对象
+ UnmanagedObjects = new ConcurrentBag();
+ Assemblies = GetAssemblies();
+ EffectiveTypes = Assemblies.SelectMany(GetTypes);
+ }
+ ///
+ /// 服务集合
+ ///
+ public static IServiceCollection Services { get; set; }
+
+ ///
+ /// 根服务
+ ///
+ public static IServiceProvider RootServices { get; set; }
+
+ public static IConfiguration Configuration { get; set; }
+
+ public static IWebHostEnvironment HostingEnvironment { get; set; }
+
+ ///
+ /// 未托管的对象集合
+ ///
+ public static readonly ConcurrentBag UnmanagedObjects;
+
+ public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext;
+
+ public static SystemConfig SystemConfig { get; set; }
+
+
+ ///
+ /// 应用有效程序集(类型为project,项目应用的,不包括包和手动应用)
+ ///
+ public static readonly IEnumerable Assemblies;
+ ///
+ /// 有效程序集类型
+ ///
+ public static readonly IEnumerable EffectiveTypes;
+ ///
+ /// 获取请求生存周期的服务(未注册返回null)
+ ///
+ ///
+ ///
+ ///
+ public static TService GetService(IServiceProvider serviceProvider = null) where TService : class
+ {
+ return GetService(typeof(TService), serviceProvider) as TService;
+ }
+
+ ///
+ /// 获取请求生存周期的服务(未注册返回null)
+ ///
+ ///
+ ///
+ ///
+ public static object GetService(Type type, IServiceProvider serviceProvider = null)
+ {
+ return (serviceProvider ?? GetServiceProvider(type)).GetService(type);
+ }
+
+ ///
+ /// 获取请求生存周期的服务(未注册异常)
+ ///
+ ///
+ ///
+ ///
+ public static TService GetRequiredService(IServiceProvider serviceProvider = null) where TService : class
+ {
+ return GetRequiredService(typeof(TService), serviceProvider) as TService;
+ }
+
+ ///
+ /// 获取请求生存周期的服务(未注册异常)
+ ///
+ ///
+ ///
+ ///
+ public static object GetRequiredService(Type type, IServiceProvider serviceProvider = null)
+ {
+ return (serviceProvider ?? GetServiceProvider(type)).GetRequiredService(type);
+ }
+
+ ///
+ /// 获取服务注册器
+ ///
+ ///
+ ///
+ public static IServiceProvider GetServiceProvider(Type serviceType)
+ {
+ if (HostingEnvironment == null)
+ {
+ return RootServices;
+ }
+ if (RootServices != null && Services.Where((ServiceDescriptor u) => u.ServiceType == (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType)).Any((ServiceDescriptor u) => u.Lifetime == ServiceLifetime.Singleton))
+ {
+ return RootServices;
+ }
+ // 第二选择是获取 HttpContext 对象的 RequestServices
+ var httpContext = HttpContext;
+ if (httpContext?.RequestServices != null) return httpContext.RequestServices;
+ // 第三选择,创建新的作用域并返回服务提供器
+ else if (RootServices != null)
+ {
+ var scoped = RootServices.CreateScope();
+ UnmanagedObjects.Add(scoped);
+ return scoped.ServiceProvider;
+ }
+ // 第四选择,构建新的服务对象(性能最差)
+ else
+ {
+ var serviceProvider = Services.BuildServiceProvider();
+ UnmanagedObjects.Add(serviceProvider);
+ return serviceProvider;
+ }
+ }
+
+
+ ///
+ /// GC 回收默认间隔
+ ///
+ private const int GC_COLLECT_INTERVAL_SECONDS = 5;
+
+ ///
+ /// 记录最近 GC 回收时间
+ ///
+ private static DateTime? LastGCCollectTime { get; set; }
+
+ ///
+ /// 释放所有未托管的对象
+ ///
+ public static void DisposeUnmanagedObjects()
+ {
+ foreach (var dsp in UnmanagedObjects)
+ {
+ try
+ {
+ dsp?.Dispose();
+ }
+ finally { }
+ }
+
+ // 强制手动回收 GC 内存
+ if (UnmanagedObjects.Any())
+ {
+ var nowTime = DateTime.UtcNow;
+ if ((LastGCCollectTime == null || (nowTime - LastGCCollectTime.Value).TotalSeconds > GC_COLLECT_INTERVAL_SECONDS))
+ {
+ LastGCCollectTime = nowTime;
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+ }
+
+ UnmanagedObjects.Clear();
+ }
+ ///
+ /// 获取版本号
+ ///
+ ///
+ public static string GetVersion()
+ {
+ Version version = Assembly.GetEntryAssembly().GetName().Version;
+ return version.ToString();
+ }
+ ///
+ /// 获取请求跟踪 Id
+ ///
+ ///
+ public static string GetTraceId()
+ {
+ return Activity.Current?.Id ?? (RootServices == null ? default : HttpContext?.TraceIdentifier);
+ }
+ ///
+ /// 程序启动时,记录目录
+ ///
+ ///
+ public static void LogWhenStart(IWebHostEnvironment env)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("程序启动");
+ sb.AppendLine("ContentRootPath:" + env.ContentRootPath);
+ sb.AppendLine("WebRootPath:" + env.WebRootPath);
+ sb.AppendLine("IsDevelopment:" + env.IsDevelopment());
+ LogHelper.WriteWithTime(sb.ToString());
+ }
+
+ ///
+ /// 设置cache control
+ ///
+ ///
+ public static void SetCacheControl(StaticFileResponseContext context)
+ {
+ int second = 365 * 24 * 60 * 60;
+ context.Context.Response.Headers.Add("Cache-Control", new[] { "public,max-age=" + second });
+ context.Context.Response.Headers.Add("Expires", new[] { DateTime.UtcNow.AddYears(1).ToString("R") }); // Format RFC1123
+ }
+ public static IEnumerable GetAssemblies()
+ {
+ var projects = DependencyContext
+ .Default
+ .RuntimeLibraries
+ .Where(u => u.Type == "project" || u.Type == "reference")
+ .Select(u => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(u.Name)));
+
+ return projects;
+ }
+ ///
+ /// 加载程序集中的所有类型
+ ///
+ ///
+ ///
+ private static IEnumerable GetTypes(Assembly ass)
+ {
+ var types = Array.Empty();
+
+ try
+ {
+ types = ass.GetTypes();
+ }
+ catch
+ {
+ Console.WriteLine($"Error load `{ass.FullName}` assembly.");
+ }
+
+ return types.Where(u => u.IsPublic);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/LayUI/FilterSo.cs b/WaterCloud.Code/LayUI/FilterSo.cs
new file mode 100644
index 0000000..f2625f0
--- /dev/null
+++ b/WaterCloud.Code/LayUI/FilterSo.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ public class FilterSo
+ {
+ /**
+ * 唯一id
+ */
+ public long id { get; set; }
+ /**
+ * 前缀 and、or
+ */
+ public string prefix { get; set; }
+ /**
+ * 模式 in、condition、date
+ */
+ public string mode { get; set; }
+ /**
+ * 字段名
+ */
+ public string field { get; set; }
+ /**
+ * 筛选类型
+ */
+ public string type { get; set; }
+ /**
+ * 是否有分隔符
+ */
+ public string split { get; set; }
+ /**
+ * 筛选值
+ */
+ public string value { get; set; }
+ /**
+ * 筛选值
+ */
+ public List values { get; set; }
+
+ /**
+ * 子组数据
+ */
+ public List children { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/LayUI/SoulPage.cs b/WaterCloud.Code/LayUI/SoulPage.cs
new file mode 100644
index 0000000..902bb02
--- /dev/null
+++ b/WaterCloud.Code/LayUI/SoulPage.cs
@@ -0,0 +1,210 @@
+using Serenity;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WaterCloud.Code
+{
+ ///
+ ///封装table查询数据
+ ///
+ ///
+ public class SoulPage
+ {
+ /**
+ * layui表格必须参数⬇⬇⬇⬇⬇⬇
+ */
+ public int state { get; set; }
+ public string message { get; set; }
+ /**
+ * 总记录
+ */
+ public int count { get; set; }
+ /**
+ * 显示的记录
+ */
+ public List data { get; set; }
+
+ /**
+ * 请求条件
+ */
+ public object obj { get; set; }
+ /**
+ * 查询条件
+ */
+ public Dictionary condition = new Dictionary();
+ /**
+ * 请求参数⬇⬇⬇⬇⬇⬇
+ */
+ /**
+ * 当前页 从1开始
+ */
+ public int page { get; set; }
+ /**
+ * 页大小
+ */
+ public int rows { get; set; }
+ /**
+ * 查询列数据
+ */
+ public string columns { get; set; }
+
+ /**
+ * 表格列类型
+ */
+ public string tableFilterType { get; set; }
+ /**
+ * 筛选信息
+ */
+ public string filterSos { get; set; }
+
+ /**
+ * 排序信息
+ */
+ public string field { get; set; }
+
+ public string order { get; set; }
+
+ public SoulPage()
+ {
+ this.state = 0;
+ this.message = "";
+ this.page = 1;
+ this.rows = 100000000;
+ this.order = "asc";
+ }
+
+ public SoulPage(int page, int limit)
+ {
+ this.state = 0;
+ this.message = "";
+ this.page = 1;
+ this.rows = 100000000;
+ this.order = "asc";
+ this.page = page;
+ this.rows = limit;
+ }
+
+ public List getFilterSos()
+ {
+ if (string.IsNullOrEmpty(filterSos))
+ {
+ return new List();
+ }
+ return filterSos.ToObject>();
+ }
+
+ public bool isColumn()
+ {
+ return getColumns().Count > 0;
+ }
+
+ public int getOffset()
+ {
+ return (page - 1) * rows;
+ }
+
+ public List getColumns()
+ {
+ return !string.IsNullOrEmpty(columns) ? columns.ToObject>() : new List();
+ }
+
+ private string dateFormat(DateTime date, string format)
+ {
+ if (string.IsNullOrEmpty(format))
+ {
+ return date.ToString("yyyy-MM-dd HH:mm:ss");
+ }
+ else
+ {
+ return date.ToString(format);
+ }
+ }
+
+ public Dictionary> getTypeMap()
+ {
+ Dictionary> typeMap = new Dictionary>();
+ if (!string.IsNullOrEmpty(tableFilterType))
+ {
+ Dictionary filterType = tableFilterType.ToObject>();
+ foreach (var item in filterType)
+ {
+ Dictionary map = new Dictionary();
+ map.Add("type", item.Value.Substring(0, item.Value.IndexOf("[")));
+ int IndexofA = item.Value.IndexOf('['); //字符串的话总以第一位为指定位置
+ int IndexofB = item.Value.IndexOf(']');
+ map.Add("value", item.Value.Substring(IndexofA + 1, IndexofB - IndexofA - 1));
+ typeMap.Add(item.Key, map);
+ };
+ }
+ return typeMap;
+ }
+
+ public string getFormatValue(Dictionary> typeMap, string column, object columnObject)
+ {
+ string columnValue;
+ if (typeMap.ContainsKey(column))
+ {
+ if ("date".Equals(typeMap.Get(column).Get("type")) && columnObject is DateTime)
+ {
+ columnValue = dateFormat((DateTime)columnObject, typeMap.Get(column).Get("value"));
+ }
+ else
+ {
+ columnValue = columnObject.ToString();
+ }
+ }
+ else
+ {
+ if (columnObject is DateTime || columnObject is Nullable)
+ {
+ columnValue = dateFormat((DateTime)columnObject, null);
+ }
+ else if (columnObject is bool || columnObject is Nullable)
+ {
+ columnValue = (bool)columnObject == true ? "1" : "0";
+ }
+ else
+ {
+ columnValue = columnObject.ToString();
+ }
+ }
+ return columnValue;
+ }
+
+ public Object setData(List data)
+ {
+ if (isColumn())
+ {
+ Dictionary> typeMap = getTypeMap();
+ Dictionary> columnMap = new Dictionary>();
+ foreach (T datum in data)
+ {
+ foreach (string column in getColumns())
+ {
+ if (!columnMap.ContainsKey(column))
+ {
+ columnMap.Add(column, new HashSet());
+ }
+ var columnObject = ReflectionHelper.GetObjectPropertyValue(datum, column);
+ if (columnObject != null)
+ { //空值不展示
+ columnMap.Get(column).Add(getFormatValue(typeMap, column, columnObject));
+ }
+ }
+ }
+ Dictionary> columnSortMap = new Dictionary>();
+ foreach (var item in columnMap)
+ {
+ columnSortMap.Add(item.Key, item.Value.ToList());
+ }
+ return columnSortMap;
+ }
+ else
+ {
+ this.data = data;
+ return this;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/LayUI/TableCols.cs b/WaterCloud.Code/LayUI/TableCols.cs
new file mode 100644
index 0000000..1143a70
--- /dev/null
+++ b/WaterCloud.Code/LayUI/TableCols.cs
@@ -0,0 +1,83 @@
+namespace WaterCloud.Code
+{
+ ///
+ /// LayUI Table 列
+ ///
+ public class TableCols
+ {
+ ///
+ /// 类型(normal(常规列)、checkbox(复选框)、radio(单选框)、numbers(序号)、space(空))
+ ///
+ public string type { get; set; }
+
+ ///
+ /// 字段
+ ///
+ public string field { get; set; }
+
+ ///
+ /// 标题
+ ///
+ public string title { get; set; }
+
+ ///
+ /// 宽度
+ ///
+ public int? width { get; set; }
+
+ ///
+ /// 最小宽度
+ ///
+ public int? minWidth { get; set; }
+
+ ///
+ /// 是否全选
+ ///
+ public bool? LAY_CHECKED { get; set; }
+
+ ///
+ /// 固定列
+ ///
+ public string Fixed { get; set; }
+
+ ///
+ /// 隐藏
+ ///
+ public string hide { get; set; }
+
+ ///
+ /// 排序
+ ///
+ public bool? sort { get; set; }
+
+ ///
+ /// 是否禁用拖到列
+ ///
+ public bool? unresize { get; set; }
+
+ ///
+ /// 样式
+ ///
+ public string style { get; set; }
+
+ ///
+ /// 对齐方式
+ ///
+ public string align { get; set; }
+
+ ///
+ /// 所占列数
+ ///
+ public int? colspan { get; set; }
+
+ ///
+ /// 所占行数
+ ///
+ public int? rowspan { get; set; }
+
+ ///
+ /// 绑定工具栏模板
+ ///
+ public string toolbar { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/ApiToken.cs b/WaterCloud.Code/Model/ApiToken.cs
new file mode 100644
index 0000000..fe7813b
--- /dev/null
+++ b/WaterCloud.Code/Model/ApiToken.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace WaterCloud.Code.Model
+{
+ ///
+ /// 这个是移动端Api用的
+ ///
+ public class BaseApiToken
+ {
+ [NotMapped]
+ [Description("WebApi没有Cookie和Session,所以需要传入Token来标识用户身份,请加在Url后面")]
+ public string Token { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/AppLogEntity.cs b/WaterCloud.Code/Model/AppLogEntity.cs
new file mode 100644
index 0000000..857cad7
--- /dev/null
+++ b/WaterCloud.Code/Model/AppLogEntity.cs
@@ -0,0 +1,7 @@
+namespace WaterCloud.Code.Model
+{
+ public class AppLogEntity
+ {
+ public string FileName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/DbLogType.cs b/WaterCloud.Code/Model/DbLogType.cs
new file mode 100644
index 0000000..c1d7c24
--- /dev/null
+++ b/WaterCloud.Code/Model/DbLogType.cs
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright © 2020 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System.ComponentModel;
+
+namespace WaterCloud.Code
+{
+ public enum DbLogType
+ {
+ [Description("其他")]
+ Other = 0,
+
+ [Description("登录")]
+ Login = 1,
+
+ [Description("退出")]
+ Exit = 2,
+
+ [Description("访问")]
+ Visit = 3,
+
+ [Description("新增")]
+ Create = 4,
+
+ [Description("删除")]
+ Delete = 5,
+
+ [Description("修改")]
+ Update = 6,
+
+ [Description("提交")]
+ Submit = 7,
+
+ [Description("异常")]
+ Exception = 8,
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/Define.cs b/WaterCloud.Code/Model/Define.cs
new file mode 100644
index 0000000..fc11849
--- /dev/null
+++ b/WaterCloud.Code/Model/Define.cs
@@ -0,0 +1,18 @@
+namespace WaterCloud.Code
+{
+ public static class Define
+ {
+ public const string PROVIDER_COOKIE = "Cookie";
+ public const string PROVIDER_SESSION = "Session";
+ public const string PROVIDER_WEBAPI = "WebApi";
+
+ public const string CACHEPROVIDER_REDIS = "Redis";
+ public const string CACHEPROVIDER_MEMORY = "Memory";
+
+ public const string DATAPRIVILEGE_LOGINUSER = "{loginUser}"; //数据权限配置中,当前登录用户的key
+ public const string DATAPRIVILEGE_LOGINROLE = "{loginRole}"; //数据权限配置中,当前登录用户角色的key
+ public const string DATAPRIVILEGE_LOGINORG = "{loginOrg}"; //数据权限配置中,当前登录用户部门的key
+ public const string SQL_MORE = "MoreSql";//多库
+ public const string SQL_TENANT = "TenantSql";//多租户
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/Filter.cs b/WaterCloud.Code/Model/Filter.cs
new file mode 100644
index 0000000..320c905
--- /dev/null
+++ b/WaterCloud.Code/Model/Filter.cs
@@ -0,0 +1,22 @@
+namespace WaterCloud.Code
+{
+ public class Filter
+ {
+ public string Key { get; set; }
+ public string Value { get; set; }
+ public string Contrast { get; set; }
+
+ public string Text { get; set; }
+ }
+
+ public class FilterList
+ {
+ ///
+ /// and
+ ///
+ public string Operation { get; set; }
+
+ public string Filters { get; set; }
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/KeyValue.cs b/WaterCloud.Code/Model/KeyValue.cs
new file mode 100644
index 0000000..441c641
--- /dev/null
+++ b/WaterCloud.Code/Model/KeyValue.cs
@@ -0,0 +1,9 @@
+namespace WaterCloud.Code
+{
+ public class KeyValue
+ {
+ public string Key { get; set; }
+ public string Value { get; set; }
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Model/PrintEntity.cs b/WaterCloud.Code/Model/PrintEntity.cs
new file mode 100644
index 0000000..9cce8e9
--- /dev/null
+++ b/WaterCloud.Code/Model/PrintEntity.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code.Model
+{
+ public class PrintEntity
+ {
+ public string cmd { get; set; } = "print";
+ public string requestId { get; set; }
+ public PrintDetail data { get; set; }
+ }
+ public class PrintDetail
+ {
+ public PrintInitInfo printIniInfo { get; set; }
+ public object data { get; set; }
+ }
+ public class PrintInitInfo
+ {
+ public string filePath { get; set; }
+ public string realName { get; set; }
+ public int? printType { get; set; } = 1;
+ public string printName { get; set; } = "";
+ public bool landscape { get; set; } = true;
+ public string paperSize { get; set; }
+ public string duplex { get; set; }
+ public bool? isBatch { get; set; } = false;
+ }
+}
diff --git a/WaterCloud.Code/Model/SystemConfig.cs b/WaterCloud.Code/Model/SystemConfig.cs
new file mode 100644
index 0000000..f02f396
--- /dev/null
+++ b/WaterCloud.Code/Model/SystemConfig.cs
@@ -0,0 +1,185 @@
+using System.Collections.Generic;
+
+namespace WaterCloud.Code.Model
+{
+ public class SystemConfig
+ {
+ ///
+ /// 是否是Demo模式
+ ///
+ public bool Demo { get; set; }
+
+ ///
+ /// 是否是调试模式
+ ///
+ public bool Debug { get; set; }
+
+ ///
+ /// 允许一个用户在多个电脑同时登录
+ ///
+ public bool LoginMultiple { get; set; }
+
+ ///
+ /// 允许跨域的站点
+ ///
+ public string AllowCorsSite { get; set; }
+
+ ///
+ /// 主库数据库类型
+ ///
+ public string DBProvider { get; set; }
+
+ ///
+ /// 主库数据库连接
+ ///
+ public string DBConnectionString { get; set; }
+
+ ///
+ /// 数据库连接超时
+ ///
+ public int DBCommandTimeout { get; set; }
+
+ ///
+ /// 是否初始化数据库
+ ///
+ public bool IsInitDb { get; set; }
+
+ ///
+ /// 是否初始化种子数据
+ ///
+ public bool IsSeedData { get; set; }
+
+ ///
+ /// 缓存类型
+ ///
+ public string CacheProvider { get; set; }
+
+ ///
+ /// redis连接串
+ ///
+ public string RedisConnectionString { get; set; }
+
+ ///
+ /// api token名称
+ ///
+ public string TokenName { get; set; }
+
+ //缓存过期时间
+ public int LoginExpire { get; set; }
+
+ ///
+ /// 主页
+ ///
+ public string HomePage { get; set; }
+
+ ///
+ /// 是否局域网
+ ///
+ public bool? LocalLAN { get; set; }
+
+ ///
+ /// 数据库模式
+ ///
+ public string SqlMode { get; set; }
+
+ ///
+ /// 项目前缀
+ ///
+ public string ProjectPrefix { get; set; }
+
+ ///
+ /// 是否重置密码
+ ///
+ public bool? ReviseSystem { get; set; }
+
+ ///
+ /// 登录错误次数
+ ///
+ public int? LoginErrorCount { get; set; }
+
+ ///
+ /// 多数据库组
+ ///
+ public List SqlConfig { get; set; }
+
+ ///
+ /// 是否集群
+ ///
+ public bool? IsCluster { get; set; }
+
+ ///
+ /// 是否删除定时调度任务
+ ///
+ public bool? NeedClear { get; set; }
+
+ ///
+ /// 主程序数据库编号
+ ///
+ public string MainDbNumber { get; set; }
+
+ ///
+ /// 是否开启定时任务
+ ///
+ public bool? OpenQuartz { get; set; }
+
+ ///
+ /// api文档配置
+ ///
+ public DocumentSettings DocumentSettings { get; set; }
+
+ ///
+ /// rabbitmq配置
+ ///
+ public MqConfig RabbitMq { get; set; }
+ }
+
+ public class DocumentSettings
+ {
+ ///
+ /// 标题
+ ///
+ public string DocumentTitle { get; set; }
+ public List GroupOpenApiInfos { get; set; }
+ }
+
+ public class GroupOpenApiInfo
+ {
+ ///
+ /// 分组
+ ///
+ public string Group { get; set; }
+
+ ///
+ /// 组标题
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; }
+
+ ///
+ /// 版本
+ ///
+ public string Version { get; set; }
+ }
+
+ public class DBConfig
+ {
+ ///
+ /// 数据库序号
+ ///
+ public string DBNumber { get; set; }
+
+ ///
+ /// 数据库类型
+ ///
+ public string DBProvider { get; set; }
+
+ ///
+ /// 数据库连接
+ ///
+ public string DBConnectionString { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Operator/OperatorModel.cs b/WaterCloud.Code/Operator/OperatorModel.cs
new file mode 100644
index 0000000..ace445f
--- /dev/null
+++ b/WaterCloud.Code/Operator/OperatorModel.cs
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// 当前用户信息
+ ///
+ public class OperatorModel
+ {
+ public string UserId { get; set; }
+ public string UserCode { get; set; }
+ public string UserName { get; set; }
+ public string UserPwd { get; set; }
+ public string CompanyId { get; set; }
+ public string OrganizeId { get; set; }
+ public string RoleId { get; set; }
+ public string LoginIPAddress { get; set; }
+ public string LoginIPAddressName { get; set; }
+ public string LoginToken { get; set; }
+ public DateTime LoginTime { get; set; }
+
+ //超管
+ public bool IsSuperAdmin { get; set; }
+
+ public bool IsAdmin { get; set; }
+ public bool IsBoss { get; set; }
+ public bool IsSenior { get; set; }
+ public bool IsSaleman { get; set; }
+
+ // 拓展字段,2019-03-03
+ public string DdUserId { get; set; }
+
+ public string WxOpenId { get; set; }
+ public string Avatar { get; set; }
+ public string loginMark { get; set; }
+
+ //扩展字段 数据库序号,2021-05-12
+ public string DbNumber { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Operator/OperatorProvider.cs b/WaterCloud.Code/Operator/OperatorProvider.cs
new file mode 100644
index 0000000..ffa6de9
--- /dev/null
+++ b/WaterCloud.Code/Operator/OperatorProvider.cs
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ public class OperatorProvider
+ {
+ //是否允许一个账户在多处登录
+ private static bool LoginMultiple = GlobalContext.SystemConfig.LoginMultiple;
+
+ //缓存过期时间
+ private static int LoginExpire = GlobalContext.SystemConfig.LoginExpire;
+
+ private static string projectPrefix = GlobalContext.SystemConfig.ProjectPrefix;
+
+ public static OperatorProvider Provider
+ {
+ get { return new OperatorProvider(); }
+ }
+
+ //watercloud_operator_pc_ PC端登录
+ //watercloud_operator_info_ 登录次数
+ //
+ ///
+ /// 缓存操作类
+ ///
+ private string cacheKeyOperator = projectPrefix + "_operator_";// +登录者token
+
+ private string cacheKeyToken = projectPrefix + "_token_";// +登录者token
+ private string cacheKeyError = projectPrefix + "_error_";// + Mark
+
+ ///
+ /// 秘钥
+ ///
+ private string LoginUserToken = projectPrefix + "_Token";
+
+ ///
+ /// 标记登录的浏览器
+ ///
+ private string LoginUserMarkKey = projectPrefix + "_Mark";
+
+ public string GetProvider(string key)
+ {
+ var token = GetToken();
+ if (!string.IsNullOrEmpty(token))
+ return token;
+ token = WebHelper.GetCookie(key).ToString();
+ if (!string.IsNullOrEmpty(token))
+ return token;
+ return WebHelper.GetSession(key).ToString();
+ }
+
+ public void SetProvider(string key, string value)
+ {
+ WebHelper.WriteCookie(key, value);
+ WebHelper.WriteSession(key, value);
+ }
+
+ public string GetToken()
+ {
+ try
+ {
+ if (GlobalContext.HttpContext == null)
+ {
+ return null;
+ }
+ //查请求头
+ string token = GlobalContext.HttpContext.Request.Headers[GlobalContext.SystemConfig.TokenName].ParseToString();
+ if (!String.IsNullOrEmpty(token)) return token;
+
+ //查参数
+ token = GlobalContext.HttpContext.Request.Query[GlobalContext.SystemConfig.TokenName];
+ if (!String.IsNullOrEmpty(token)) return token;
+
+ //查cookies
+ string cookie = GlobalContext.HttpContext.Request.Cookies[GlobalContext.SystemConfig.TokenName];
+ return cookie == null ? string.Empty : cookie;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public void RemoveProvider(string key)
+ {
+ WebHelper.RemoveCookie(key);
+ WebHelper.RemoveSession(key);
+ }
+
+ public OperatorModel GetCurrent()
+ {
+ OperatorModel operatorModel = new OperatorModel();
+ try
+ {
+ string loginMark = GetProvider(LoginUserMarkKey);
+ operatorModel = CacheHelper.Get(cacheKeyOperator + loginMark);
+ }
+ catch
+ {
+ operatorModel = null;
+ }
+ return operatorModel;
+ }
+
+ ///
+ /// 获取浏览器设配号
+ ///
+ ///
+ public string GetMark()
+ {
+ string cookieMark = GetProvider(LoginUserMarkKey);
+ if (string.IsNullOrEmpty(cookieMark))
+ {
+ cookieMark = Guid.NewGuid().ToString();
+ SetProvider(LoginUserMarkKey, cookieMark);
+ }
+ return cookieMark;
+ }
+
+ ///
+ /// 登录者信息添加到缓存中
+ ///
+ /// 用户
+ /// 设备标识uid
+ /// 设备类型
+ /// 是否保存cookie,默认是
+ ///
+ public async Task AddLoginUser(OperatorModel operatorModel, string loginMark, string facilityMark)
+ {
+ string token = Guid.NewGuid().ToString();
+ try
+ {
+ // 填写登录信息
+ operatorModel.LoginToken = token;
+ //登录信息更新
+ if (string.IsNullOrEmpty(loginMark))
+ {
+ string cookieMark = GetProvider(LoginUserMarkKey);
+ if (string.IsNullOrEmpty(cookieMark))
+ {
+ operatorModel.loginMark = Guid.NewGuid().ToString();
+ SetProvider(LoginUserMarkKey, operatorModel.loginMark);
+ }
+ else
+ {
+ operatorModel.loginMark = cookieMark;
+ }
+ SetProvider(LoginUserToken, token);
+ }
+ else
+ {
+ operatorModel.loginMark = loginMark;
+ RemoveProvider(LoginUserMarkKey);
+ }
+ //redis 登录token列表更新
+ Dictionary tokenMarkList = await CacheHelper.GetAsync>(cacheKeyToken + operatorModel.UserId);
+ if (tokenMarkList == null)// 此账号第一次登录
+ {
+ tokenMarkList = new Dictionary();
+ tokenMarkList.Add(operatorModel.loginMark, token);
+ }
+ else
+ {
+ if (tokenMarkList.ContainsKey(operatorModel.loginMark))
+ {
+ tokenMarkList[operatorModel.loginMark] = token;
+ }
+ else
+ {
+ tokenMarkList.Add(operatorModel.loginMark, token);
+ }
+ }
+
+ await CacheHelper.SetAsync(cacheKeyToken + operatorModel.UserId, tokenMarkList);
+ await CacheHelper.SetAsync(cacheKeyOperator + operatorModel.loginMark, operatorModel, LoginExpire);
+ await CacheHelper.RemoveAsync(cacheKeyOperator + facilityMark + operatorModel.UserId);
+ await CacheHelper.SetAsync(cacheKeyOperator + facilityMark + operatorModel.UserId, token, LoginExpire);
+ return token;
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// 清空当前登录信息
+ ///
+ /// apitoken
+ /// 设备类型
+ public async Task EmptyCurrent(string facilityMark)
+ {
+ try
+ {
+ string token = GetProvider(LoginUserToken);
+ string loginMark = GetProvider(LoginUserMarkKey);
+ await EmptyCurrent(token, facilityMark, loginMark);
+ RemoveProvider(LoginUserMarkKey.Trim());
+ RemoveProvider(LoginUserToken.Trim());
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ ///
+ /// 清空当前登录信息
+ ///
+ /// 登录票据
+ /// 登录设备
+ /// 登录设备标识
+ public async Task EmptyCurrent(string token, string facilityMark, string loginMark)
+ {
+ try
+ {
+ OperatorModel operatorInfo = await CacheHelper.GetAsync(cacheKeyOperator + loginMark);
+ if (operatorInfo != null)
+ {
+ Dictionary tokenMarkList = await CacheHelper.GetAsync>(cacheKeyToken + operatorInfo.UserId);
+ tokenMarkList.Remove(loginMark);
+ await CacheHelper.RemoveAsync(cacheKeyOperator + loginMark);
+ if (operatorInfo.LoginToken == token || facilityMark == "api_")
+ {
+ await CacheHelper.RemoveAsync(cacheKeyOperator + facilityMark + operatorInfo.UserId);
+ }
+ await CacheHelper.SetAsync(cacheKeyToken + operatorInfo.UserId, tokenMarkList);
+ await CacheHelper.RemoveAsync(facilityMark + GlobalContext.SystemConfig.TokenName + "_" + operatorInfo.UserId + "_" + operatorInfo.LoginTime);
+ }
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ ///
+ /// 判断登录状态
+ ///
+ /// 登录设备
+ /// apitoken
+ /// -1未登录,1登录成功,0登录过期,-2账号被顶
+ public async Task IsOnLine(string facilityMark)
+ {
+ try
+ {
+ string token = GetProvider(LoginUserToken);
+ string loginMark = GetProvider(LoginUserMarkKey);
+ return await IsOnLine(token, facilityMark, loginMark);
+ }
+ catch (Exception)
+ {
+ return new OperatorResult { stateCode = -1 };
+ }
+ }
+
+ ///
+ /// 判断登录状态
+ ///
+ /// 登录票据
+ /// 登录设备
+ /// 登录设备标识
+ /// -1未登录,1登录成功,0登录过期,-2账号被顶
+ public async Task IsOnLine(string token, string facilityMark, string loginMark)
+ {
+ OperatorResult operatorResult = new OperatorResult();
+ operatorResult.stateCode = -1; // -1未登录,1登录成功,0登录过期
+ try
+ {
+ if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(loginMark))
+ {
+ return operatorResult;
+ }
+ OperatorModel operatorInfo = await CacheHelper.GetAsync(cacheKeyOperator + loginMark);
+ if (operatorInfo != null)
+ {
+ Dictionary tokenMarkList = await CacheHelper.GetAsync>(cacheKeyToken + operatorInfo.UserId);
+ if ((token == operatorInfo.LoginToken || facilityMark == "api_") && tokenMarkList.ContainsKey(operatorInfo.loginMark) && tokenMarkList[operatorInfo.loginMark] == operatorInfo.LoginToken)
+ {
+ ////账号被顶(排除admin)
+ if (!LoginMultiple && !operatorInfo.IsSuperAdmin && operatorInfo.LoginToken != await CacheHelper.GetAsync(cacheKeyOperator + facilityMark + operatorInfo.UserId))
+ {
+ operatorResult.stateCode = -2;
+ tokenMarkList = await CacheHelper.GetAsync>(cacheKeyToken + operatorInfo.UserId);
+ tokenMarkList.Remove(loginMark);
+ await CacheHelper.SetAsync(cacheKeyToken + operatorInfo.UserId, tokenMarkList);
+ await CacheHelper.RemoveAsync(cacheKeyOperator + loginMark);
+ }
+ else
+ {
+ operatorResult.userInfo = operatorInfo;
+ operatorResult.stateCode = 1;
+ await CacheHelper.ExpireAsync(cacheKeyOperator + loginMark, LoginExpire);
+ await CacheHelper.ExpireAsync(cacheKeyOperator + facilityMark + operatorInfo.UserId, LoginExpire);
+ await CacheHelper.ExpireAsync(facilityMark + GlobalContext.SystemConfig.TokenName + "_" + operatorInfo.UserId + "_" + operatorInfo.LoginTime, LoginExpire);
+ }
+ }
+ }
+ return operatorResult;
+ }
+ catch (Exception)
+ {
+ return operatorResult;
+ }
+ }
+
+ #region 登录错误次数记录
+
+ ///
+ /// 获取当前登录错误次数
+ ///
+ ///
+ public async Task GetCurrentErrorNum()
+ {
+ int res = 0;
+ try
+ {
+ string cookieMark = GetProvider(LoginUserMarkKey);
+ if (string.IsNullOrEmpty(cookieMark))
+ {
+ cookieMark = Guid.NewGuid().ToString();
+ SetProvider(LoginUserMarkKey, cookieMark);
+ }
+ string num = await CacheHelper.GetAsync(cacheKeyError + cookieMark);
+ if (!string.IsNullOrEmpty(num))
+ {
+ res = Convert.ToInt32(num);
+ }
+ }
+ catch (Exception)
+ {
+ }
+ return res;
+ }
+
+ ///
+ /// 增加错误次数
+ ///
+ ///
+ public async Task AddCurrentErrorNum()
+ {
+ int res = 0;
+ try
+ {
+ string cookieMark = GetProvider(LoginUserMarkKey);
+ if (string.IsNullOrEmpty(cookieMark))
+ {
+ cookieMark = Guid.NewGuid().ToString();
+ SetProvider(LoginUserMarkKey, cookieMark);
+ }
+ string num = await CacheHelper.GetAsync(cacheKeyError + cookieMark);
+ if (!string.IsNullOrEmpty(num))
+ {
+ res = Convert.ToInt32(num);
+ }
+ res++;
+ num = res + "";
+ await CacheHelper.SetAsync(cacheKeyError + cookieMark, num, 24);
+ }
+ catch (Exception)
+ {
+ }
+ return res;
+ }
+
+ ///
+ /// 清除当前登录错误次数
+ ///
+ public async Task ClearCurrentErrorNum()
+ {
+ try
+ {
+ string cookieMark = GetProvider(LoginUserMarkKey);
+ if (string.IsNullOrEmpty(cookieMark))
+ {
+ cookieMark = Guid.NewGuid().ToString();
+ SetProvider(LoginUserMarkKey, cookieMark);
+ }
+ await CacheHelper.RemoveAsync(cacheKeyError + cookieMark);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ #endregion 登录错误次数记录
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Operator/OperatorResult.cs b/WaterCloud.Code/Operator/OperatorResult.cs
new file mode 100644
index 0000000..22e43dd
--- /dev/null
+++ b/WaterCloud.Code/Operator/OperatorResult.cs
@@ -0,0 +1,15 @@
+namespace WaterCloud.Code
+{
+ public class OperatorResult
+ {
+ ///
+ /// 状态码-1未登录,1登录成功,0登录过期
+ ///
+ public int stateCode { get; set; }
+
+ ///
+ /// 登录者用户信息
+ ///
+ public OperatorModel userInfo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Operator/OperatorUserInfo.cs b/WaterCloud.Code/Operator/OperatorUserInfo.cs
new file mode 100644
index 0000000..e592d41
--- /dev/null
+++ b/WaterCloud.Code/Operator/OperatorUserInfo.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace WaterCloud.Code
+{
+ public class OperatorUserInfo
+ {
+ //密码
+ public string F_UserPassword { get; set; }
+
+ //
+ public string F_UserSecretkey { get; set; }
+
+ //登录时间设置
+ public DateTime? F_AllowStartTime { get; set; }
+
+ public DateTime? F_AllowEndTime { get; set; }
+
+ //锁定时间设置
+ public DateTime? F_LockStartDate { get; set; }
+
+ public DateTime? F_LockEndDate { get; set; }
+
+ //第一次登录
+ public DateTime? F_FirstVisitTime { get; set; }
+
+ //上一次登录时间
+ public DateTime? F_PreviousVisitTime { get; set; }
+
+ //最后一次登录时间
+ public DateTime? F_LastVisitTime { get; set; }
+
+ //修改密码时间
+ public DateTime? F_ChangePasswordDate { get; set; }
+
+ //登录次数
+ public int? F_LogOnCount { get; set; }
+
+ //在线标记
+ public bool? F_UserOnLine { get; set; }
+
+ //安全问题
+ public string F_Question { get; set; }
+
+ //问题答案
+ public string F_AnswerQuestion { get; set; }
+
+ //默认主题
+ public string F_Theme { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Properties/launchSettings.json b/WaterCloud.Code/Properties/launchSettings.json
new file mode 100644
index 0000000..4d97569
--- /dev/null
+++ b/WaterCloud.Code/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:59747/",
+ "sslPort": 44332
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "WaterCloud.Code": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Provider/CustomerFileExtensionContentTypeProvider.cs b/WaterCloud.Code/Provider/CustomerFileExtensionContentTypeProvider.cs
new file mode 100644
index 0000000..efb1dd3
--- /dev/null
+++ b/WaterCloud.Code/Provider/CustomerFileExtensionContentTypeProvider.cs
@@ -0,0 +1,1540 @@
+using Microsoft.AspNetCore.StaticFiles;
+using System;
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// mime文件
+ ///
+ public class CustomerFileExtensionContentTypeProvider : FileExtensionContentTypeProvider
+ {
+ public CustomerFileExtensionContentTypeProvider() :
+ base((IDictionary)new Dictionary(
+ (IEqualityComparer)StringComparer.OrdinalIgnoreCase)
+ {
+ {
+ ".frx",
+ "application/rtf"
+ },
+ {
+ ".323",
+ "text/h323"
+ },
+ {
+ ".3g2",
+ "video/3gpp2"
+ },
+ {
+ ".3gp2",
+ "video/3gpp2"
+ },
+ {
+ ".3gp",
+ "video/3gpp"
+ },
+ {
+ ".3gpp",
+ "video/3gpp"
+ },
+ {
+ ".aac",
+ "audio/aac"
+ },
+ {
+ ".aaf",
+ "application/octet-stream"
+ },
+ {
+ ".aca",
+ "application/octet-stream"
+ },
+ {
+ ".accdb",
+ "application/msaccess"
+ },
+ {
+ ".accde",
+ "application/msaccess"
+ },
+ {
+ ".accdt",
+ "application/msaccess"
+ },
+ {
+ ".acx",
+ "application/internet-property-stream"
+ },
+ {
+ ".adt",
+ "audio/vnd.dlna.adts"
+ },
+ {
+ ".adts",
+ "audio/vnd.dlna.adts"
+ },
+ {
+ ".afm",
+ "application/octet-stream"
+ },
+ {
+ ".ai",
+ "application/postscript"
+ },
+ {
+ ".aif",
+ "audio/x-aiff"
+ },
+ {
+ ".aifc",
+ "audio/aiff"
+ },
+ {
+ ".aiff",
+ "audio/aiff"
+ },
+ {
+ ".appcache",
+ "text/cache-manifest"
+ },
+ {
+ ".application",
+ "application/x-ms-application"
+ },
+ {
+ ".art",
+ "image/x-jg"
+ },
+ {
+ ".asd",
+ "application/octet-stream"
+ },
+ {
+ ".asf",
+ "video/x-ms-asf"
+ },
+ {
+ ".asi",
+ "application/octet-stream"
+ },
+ {
+ ".asm",
+ "text/plain"
+ },
+ {
+ ".asr",
+ "video/x-ms-asf"
+ },
+ {
+ ".asx",
+ "video/x-ms-asf"
+ },
+ {
+ ".atom",
+ "application/atom+xml"
+ },
+ {
+ ".au",
+ "audio/basic"
+ },
+ {
+ ".avi",
+ "video/x-msvideo"
+ },
+ {
+ ".axs",
+ "application/olescript"
+ },
+ {
+ ".bas",
+ "text/plain"
+ },
+ {
+ ".bcpio",
+ "application/x-bcpio"
+ },
+ {
+ ".bin",
+ "application/octet-stream"
+ },
+ {
+ ".bmp",
+ "image/bmp"
+ },
+ {
+ ".c",
+ "text/plain"
+ },
+ {
+ ".cab",
+ "application/vnd.ms-cab-compressed"
+ },
+ {
+ ".calx",
+ "application/vnd.ms-office.calx"
+ },
+ {
+ ".cat",
+ "application/vnd.ms-pki.seccat"
+ },
+ {
+ ".cdf",
+ "application/x-cdf"
+ },
+ {
+ ".chm",
+ "application/octet-stream"
+ },
+ {
+ ".class",
+ "application/x-java-applet"
+ },
+ {
+ ".clp",
+ "application/x-msclip"
+ },
+ {
+ ".cmx",
+ "image/x-cmx"
+ },
+ {
+ ".cnf",
+ "text/plain"
+ },
+ {
+ ".cod",
+ "image/cis-cod"
+ },
+ {
+ ".cpio",
+ "application/x-cpio"
+ },
+ {
+ ".cpp",
+ "text/plain"
+ },
+ {
+ ".crd",
+ "application/x-mscardfile"
+ },
+ {
+ ".crl",
+ "application/pkix-crl"
+ },
+ {
+ ".crt",
+ "application/x-x509-ca-cert"
+ },
+ {
+ ".csh",
+ "application/x-csh"
+ },
+ {
+ ".css",
+ "text/css"
+ },
+ {
+ ".csv",
+ "application/octet-stream"
+ },
+ {
+ ".cur",
+ "application/octet-stream"
+ },
+ {
+ ".dcr",
+ "application/x-director"
+ },
+ {
+ ".deploy",
+ "application/octet-stream"
+ },
+ {
+ ".der",
+ "application/x-x509-ca-cert"
+ },
+ {
+ ".dib",
+ "image/bmp"
+ },
+ {
+ ".dir",
+ "application/x-director"
+ },
+ {
+ ".disco",
+ "text/xml"
+ },
+ {
+ ".dlm",
+ "text/dlm"
+ },
+ {
+ ".doc",
+ "application/msword"
+ },
+ {
+ ".docm",
+ "application/vnd.ms-word.document.macroEnabled.12"
+ },
+ {
+ ".docx",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+ },
+ {
+ ".dot",
+ "application/msword"
+ },
+ {
+ ".dotm",
+ "application/vnd.ms-word.template.macroEnabled.12"
+ },
+ {
+ ".dotx",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
+ },
+ {
+ ".dsp",
+ "application/octet-stream"
+ },
+ {
+ ".dtd",
+ "text/xml"
+ },
+ {
+ ".dvi",
+ "application/x-dvi"
+ },
+ {
+ ".dvr-ms",
+ "video/x-ms-dvr"
+ },
+ {
+ ".dwf",
+ "drawing/x-dwf"
+ },
+ {
+ ".dwp",
+ "application/octet-stream"
+ },
+ {
+ ".dxr",
+ "application/x-director"
+ },
+ {
+ ".eml",
+ "message/rfc822"
+ },
+ {
+ ".emz",
+ "application/octet-stream"
+ },
+ {
+ ".eot",
+ "application/vnd.ms-fontobject"
+ },
+ {
+ ".eps",
+ "application/postscript"
+ },
+ {
+ ".etx",
+ "text/x-setext"
+ },
+ {
+ ".evy",
+ "application/envoy"
+ },
+ {
+ ".fdf",
+ "application/vnd.fdf"
+ },
+ {
+ ".fif",
+ "application/fractals"
+ },
+ {
+ ".fla",
+ "application/octet-stream"
+ },
+ {
+ ".flr",
+ "x-world/x-vrml"
+ },
+ {
+ ".flv",
+ "video/x-flv"
+ },
+ {
+ ".gif",
+ "image/gif"
+ },
+ {
+ ".gtar",
+ "application/x-gtar"
+ },
+ {
+ ".gz",
+ "application/x-gzip"
+ },
+ {
+ ".h",
+ "text/plain"
+ },
+ {
+ ".hdf",
+ "application/x-hdf"
+ },
+ {
+ ".hdml",
+ "text/x-hdml"
+ },
+ {
+ ".hhc",
+ "application/x-oleobject"
+ },
+ {
+ ".hhk",
+ "application/octet-stream"
+ },
+ {
+ ".hhp",
+ "application/octet-stream"
+ },
+ {
+ ".hlp",
+ "application/winhlp"
+ },
+ {
+ ".hqx",
+ "application/mac-binhex40"
+ },
+ {
+ ".hta",
+ "application/hta"
+ },
+ {
+ ".htc",
+ "text/x-component"
+ },
+ {
+ ".htm",
+ "text/html"
+ },
+ {
+ ".html",
+ "text/html"
+ },
+ {
+ ".htt",
+ "text/webviewhtml"
+ },
+ {
+ ".hxt",
+ "text/html"
+ },
+ {
+ ".ical",
+ "text/calendar"
+ },
+ {
+ ".icalendar",
+ "text/calendar"
+ },
+ {
+ ".ico",
+ "image/x-icon"
+ },
+ {
+ ".ics",
+ "text/calendar"
+ },
+ {
+ ".ief",
+ "image/ief"
+ },
+ {
+ ".ifb",
+ "text/calendar"
+ },
+ {
+ ".iii",
+ "application/x-iphone"
+ },
+ {
+ ".inf",
+ "application/octet-stream"
+ },
+ {
+ ".ins",
+ "application/x-internet-signup"
+ },
+ {
+ ".isp",
+ "application/x-internet-signup"
+ },
+ {
+ ".IVF",
+ "video/x-ivf"
+ },
+ {
+ ".jar",
+ "application/java-archive"
+ },
+ {
+ ".java",
+ "application/octet-stream"
+ },
+ {
+ ".jck",
+ "application/liquidmotion"
+ },
+ {
+ ".jcz",
+ "application/liquidmotion"
+ },
+ {
+ ".jfif",
+ "image/pjpeg"
+ },
+ {
+ ".jpb",
+ "application/octet-stream"
+ },
+ {
+ ".jpe",
+ "image/jpeg"
+ },
+ {
+ ".jpeg",
+ "image/jpeg"
+ },
+ {
+ ".jpg",
+ "image/jpeg"
+ },
+ {
+ ".js",
+ "application/javascript"
+ },
+ {
+ ".json",
+ "application/json"
+ },
+ {
+ ".jsx",
+ "text/jscript"
+ },
+ {
+ ".latex",
+ "application/x-latex"
+ },
+ {
+ ".lit",
+ "application/x-ms-reader"
+ },
+ {
+ ".lpk",
+ "application/octet-stream"
+ },
+ {
+ ".lsf",
+ "video/x-la-asf"
+ },
+ {
+ ".lsx",
+ "video/x-la-asf"
+ },
+ {
+ ".lzh",
+ "application/octet-stream"
+ },
+ {
+ ".m13",
+ "application/x-msmediaview"
+ },
+ {
+ ".m14",
+ "application/x-msmediaview"
+ },
+ {
+ ".m1v",
+ "video/mpeg"
+ },
+ {
+ ".m2ts",
+ "video/vnd.dlna.mpeg-tts"
+ },
+ {
+ ".m3u",
+ "audio/x-mpegurl"
+ },
+ {
+ ".m4a",
+ "audio/mp4"
+ },
+ {
+ ".m4v",
+ "video/mp4"
+ },
+ {
+ ".man",
+ "application/x-troff-man"
+ },
+ {
+ ".manifest",
+ "application/x-ms-manifest"
+ },
+ {
+ ".map",
+ "text/plain"
+ },
+ {
+ ".markdown",
+ "text/markdown"
+ },
+ {
+ ".md",
+ "text/markdown"
+ },
+ {
+ ".mdb",
+ "application/x-msaccess"
+ },
+ {
+ ".mdp",
+ "application/octet-stream"
+ },
+ {
+ ".me",
+ "application/x-troff-me"
+ },
+ {
+ ".mht",
+ "message/rfc822"
+ },
+ {
+ ".mhtml",
+ "message/rfc822"
+ },
+ {
+ ".mid",
+ "audio/mid"
+ },
+ {
+ ".midi",
+ "audio/mid"
+ },
+ {
+ ".mix",
+ "application/octet-stream"
+ },
+ {
+ ".mmf",
+ "application/x-smaf"
+ },
+ {
+ ".mno",
+ "text/xml"
+ },
+ {
+ ".mny",
+ "application/x-msmoney"
+ },
+ {
+ ".mov",
+ "video/quicktime"
+ },
+ {
+ ".movie",
+ "video/x-sgi-movie"
+ },
+ {
+ ".mp2",
+ "video/mpeg"
+ },
+ {
+ ".mp3",
+ "audio/mpeg"
+ },
+ {
+ ".mp4",
+ "video/mp4"
+ },
+ {
+ ".mp4v",
+ "video/mp4"
+ },
+ {
+ ".mpa",
+ "video/mpeg"
+ },
+ {
+ ".mpe",
+ "video/mpeg"
+ },
+ {
+ ".mpeg",
+ "video/mpeg"
+ },
+ {
+ ".mpg",
+ "video/mpeg"
+ },
+ {
+ ".mpp",
+ "application/vnd.ms-project"
+ },
+ {
+ ".mpv2",
+ "video/mpeg"
+ },
+ {
+ ".ms",
+ "application/x-troff-ms"
+ },
+ {
+ ".msi",
+ "application/octet-stream"
+ },
+ {
+ ".mso",
+ "application/octet-stream"
+ },
+ {
+ ".mvb",
+ "application/x-msmediaview"
+ },
+ {
+ ".mvc",
+ "application/x-miva-compiled"
+ },
+ {
+ ".nc",
+ "application/x-netcdf"
+ },
+ {
+ ".nsc",
+ "video/x-ms-asf"
+ },
+ {
+ ".nws",
+ "message/rfc822"
+ },
+ {
+ ".ocx",
+ "application/octet-stream"
+ },
+ {
+ ".oda",
+ "application/oda"
+ },
+ {
+ ".odc",
+ "text/x-ms-odc"
+ },
+ {
+ ".ods",
+ "application/oleobject"
+ },
+ {
+ ".oga",
+ "audio/ogg"
+ },
+ {
+ ".ogg",
+ "video/ogg"
+ },
+ {
+ ".ogv",
+ "video/ogg"
+ },
+ {
+ ".ogx",
+ "application/ogg"
+ },
+ {
+ ".one",
+ "application/onenote"
+ },
+ {
+ ".onea",
+ "application/onenote"
+ },
+ {
+ ".onetoc",
+ "application/onenote"
+ },
+ {
+ ".onetoc2",
+ "application/onenote"
+ },
+ {
+ ".onetmp",
+ "application/onenote"
+ },
+ {
+ ".onepkg",
+ "application/onenote"
+ },
+ {
+ ".osdx",
+ "application/opensearchdescription+xml"
+ },
+ {
+ ".otf",
+ "font/otf"
+ },
+ {
+ ".p10",
+ "application/pkcs10"
+ },
+ {
+ ".p12",
+ "application/x-pkcs12"
+ },
+ {
+ ".p7b",
+ "application/x-pkcs7-certificates"
+ },
+ {
+ ".p7c",
+ "application/pkcs7-mime"
+ },
+ {
+ ".p7m",
+ "application/pkcs7-mime"
+ },
+ {
+ ".p7r",
+ "application/x-pkcs7-certreqresp"
+ },
+ {
+ ".p7s",
+ "application/pkcs7-signature"
+ },
+ {
+ ".pbm",
+ "image/x-portable-bitmap"
+ },
+ {
+ ".pcx",
+ "application/octet-stream"
+ },
+ {
+ ".pcz",
+ "application/octet-stream"
+ },
+ {
+ ".pdf",
+ "application/pdf"
+ },
+ {
+ ".pfb",
+ "application/octet-stream"
+ },
+ {
+ ".pfm",
+ "application/octet-stream"
+ },
+ {
+ ".pfx",
+ "application/x-pkcs12"
+ },
+ {
+ ".pgm",
+ "image/x-portable-graymap"
+ },
+ {
+ ".pko",
+ "application/vnd.ms-pki.pko"
+ },
+ {
+ ".pma",
+ "application/x-perfmon"
+ },
+ {
+ ".pmc",
+ "application/x-perfmon"
+ },
+ {
+ ".pml",
+ "application/x-perfmon"
+ },
+ {
+ ".pmr",
+ "application/x-perfmon"
+ },
+ {
+ ".pmw",
+ "application/x-perfmon"
+ },
+ {
+ ".png",
+ "image/png"
+ },
+ {
+ ".pnm",
+ "image/x-portable-anymap"
+ },
+ {
+ ".pnz",
+ "image/png"
+ },
+ {
+ ".pot",
+ "application/vnd.ms-powerpoint"
+ },
+ {
+ ".potm",
+ "application/vnd.ms-powerpoint.template.macroEnabled.12"
+ },
+ {
+ ".potx",
+ "application/vnd.openxmlformats-officedocument.presentationml.template"
+ },
+ {
+ ".ppam",
+ "application/vnd.ms-powerpoint.addin.macroEnabled.12"
+ },
+ {
+ ".ppm",
+ "image/x-portable-pixmap"
+ },
+ {
+ ".pps",
+ "application/vnd.ms-powerpoint"
+ },
+ {
+ ".ppsm",
+ "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"
+ },
+ {
+ ".ppsx",
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow"
+ },
+ {
+ ".ppt",
+ "application/vnd.ms-powerpoint"
+ },
+ {
+ ".pptm",
+ "application/vnd.ms-powerpoint.presentation.macroEnabled.12"
+ },
+ {
+ ".pptx",
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+ },
+ {
+ ".prf",
+ "application/pics-rules"
+ },
+ {
+ ".prm",
+ "application/octet-stream"
+ },
+ {
+ ".prx",
+ "application/octet-stream"
+ },
+ {
+ ".ps",
+ "application/postscript"
+ },
+ {
+ ".psd",
+ "application/octet-stream"
+ },
+ {
+ ".psm",
+ "application/octet-stream"
+ },
+ {
+ ".psp",
+ "application/octet-stream"
+ },
+ {
+ ".pub",
+ "application/x-mspublisher"
+ },
+ {
+ ".qt",
+ "video/quicktime"
+ },
+ {
+ ".qtl",
+ "application/x-quicktimeplayer"
+ },
+ {
+ ".qxd",
+ "application/octet-stream"
+ },
+ {
+ ".ra",
+ "audio/x-pn-realaudio"
+ },
+ {
+ ".ram",
+ "audio/x-pn-realaudio"
+ },
+ {
+ ".rar",
+ "application/octet-stream"
+ },
+ {
+ ".ras",
+ "image/x-cmu-raster"
+ },
+ {
+ ".rf",
+ "image/vnd.rn-realflash"
+ },
+ {
+ ".rgb",
+ "image/x-rgb"
+ },
+ {
+ ".rm",
+ "application/vnd.rn-realmedia"
+ },
+ {
+ ".rmi",
+ "audio/mid"
+ },
+ {
+ ".roff",
+ "application/x-troff"
+ },
+ {
+ ".rpm",
+ "audio/x-pn-realaudio-plugin"
+ },
+ {
+ ".rtf",
+ "application/rtf"
+ },
+ {
+ ".rtx",
+ "text/richtext"
+ },
+ {
+ ".scd",
+ "application/x-msschedule"
+ },
+ {
+ ".sct",
+ "text/scriptlet"
+ },
+ {
+ ".sea",
+ "application/octet-stream"
+ },
+ {
+ ".setpay",
+ "application/set-payment-initiation"
+ },
+ {
+ ".setreg",
+ "application/set-registration-initiation"
+ },
+ {
+ ".sgml",
+ "text/sgml"
+ },
+ {
+ ".sh",
+ "application/x-sh"
+ },
+ {
+ ".shar",
+ "application/x-shar"
+ },
+ {
+ ".sit",
+ "application/x-stuffit"
+ },
+ {
+ ".sldm",
+ "application/vnd.ms-powerpoint.slide.macroEnabled.12"
+ },
+ {
+ ".sldx",
+ "application/vnd.openxmlformats-officedocument.presentationml.slide"
+ },
+ {
+ ".smd",
+ "audio/x-smd"
+ },
+ {
+ ".smi",
+ "application/octet-stream"
+ },
+ {
+ ".smx",
+ "audio/x-smd"
+ },
+ {
+ ".smz",
+ "audio/x-smd"
+ },
+ {
+ ".snd",
+ "audio/basic"
+ },
+ {
+ ".snp",
+ "application/octet-stream"
+ },
+ {
+ ".spc",
+ "application/x-pkcs7-certificates"
+ },
+ {
+ ".spl",
+ "application/futuresplash"
+ },
+ {
+ ".spx",
+ "audio/ogg"
+ },
+ {
+ ".src",
+ "application/x-wais-source"
+ },
+ {
+ ".ssm",
+ "application/streamingmedia"
+ },
+ {
+ ".sst",
+ "application/vnd.ms-pki.certstore"
+ },
+ {
+ ".stl",
+ "application/vnd.ms-pki.stl"
+ },
+ {
+ ".sv4cpio",
+ "application/x-sv4cpio"
+ },
+ {
+ ".sv4crc",
+ "application/x-sv4crc"
+ },
+ {
+ ".svg",
+ "image/svg+xml"
+ },
+ {
+ ".svgz",
+ "image/svg+xml"
+ },
+ {
+ ".swf",
+ "application/x-shockwave-flash"
+ },
+ {
+ ".t",
+ "application/x-troff"
+ },
+ {
+ ".tar",
+ "application/x-tar"
+ },
+ {
+ ".tcl",
+ "application/x-tcl"
+ },
+ {
+ ".tex",
+ "application/x-tex"
+ },
+ {
+ ".texi",
+ "application/x-texinfo"
+ },
+ {
+ ".texinfo",
+ "application/x-texinfo"
+ },
+ {
+ ".tgz",
+ "application/x-compressed"
+ },
+ {
+ ".thmx",
+ "application/vnd.ms-officetheme"
+ },
+ {
+ ".thn",
+ "application/octet-stream"
+ },
+ {
+ ".tif",
+ "image/tiff"
+ },
+ {
+ ".tiff",
+ "image/tiff"
+ },
+ {
+ ".toc",
+ "application/octet-stream"
+ },
+ {
+ ".tr",
+ "application/x-troff"
+ },
+ {
+ ".trm",
+ "application/x-msterminal"
+ },
+ {
+ ".ts",
+ "video/vnd.dlna.mpeg-tts"
+ },
+ {
+ ".tsv",
+ "text/tab-separated-values"
+ },
+ {
+ ".ttc",
+ "application/x-font-ttf"
+ },
+ {
+ ".ttf",
+ "application/x-font-ttf"
+ },
+ {
+ ".tts",
+ "video/vnd.dlna.mpeg-tts"
+ },
+ {
+ ".txt",
+ "text/plain"
+ },
+ {
+ ".u32",
+ "application/octet-stream"
+ },
+ {
+ ".uls",
+ "text/iuls"
+ },
+ {
+ ".ustar",
+ "application/x-ustar"
+ },
+ {
+ ".vbs",
+ "text/vbscript"
+ },
+ {
+ ".vcf",
+ "text/x-vcard"
+ },
+ {
+ ".vcs",
+ "text/plain"
+ },
+ {
+ ".vdx",
+ "application/vnd.ms-visio.viewer"
+ },
+ {
+ ".vml",
+ "text/xml"
+ },
+ {
+ ".vsd",
+ "application/vnd.visio"
+ },
+ {
+ ".vss",
+ "application/vnd.visio"
+ },
+ {
+ ".vst",
+ "application/vnd.visio"
+ },
+ {
+ ".vsto",
+ "application/x-ms-vsto"
+ },
+ {
+ ".vsw",
+ "application/vnd.visio"
+ },
+ {
+ ".vsx",
+ "application/vnd.visio"
+ },
+ {
+ ".vtx",
+ "application/vnd.visio"
+ },
+ {
+ ".wasm",
+ "application/wasm"
+ },
+ {
+ ".wav",
+ "audio/wav"
+ },
+ {
+ ".wax",
+ "audio/x-ms-wax"
+ },
+ {
+ ".wbmp",
+ "image/vnd.wap.wbmp"
+ },
+ {
+ ".wcm",
+ "application/vnd.ms-works"
+ },
+ {
+ ".wdb",
+ "application/vnd.ms-works"
+ },
+ {
+ ".webm",
+ "video/webm"
+ },
+ {
+ ".webp",
+ "image/webp"
+ },
+ {
+ ".wks",
+ "application/vnd.ms-works"
+ },
+ {
+ ".wm",
+ "video/x-ms-wm"
+ },
+ {
+ ".wma",
+ "audio/x-ms-wma"
+ },
+ {
+ ".wmd",
+ "application/x-ms-wmd"
+ },
+ {
+ ".wmf",
+ "application/x-msmetafile"
+ },
+ {
+ ".wml",
+ "text/vnd.wap.wml"
+ },
+ {
+ ".wmlc",
+ "application/vnd.wap.wmlc"
+ },
+ {
+ ".wmls",
+ "text/vnd.wap.wmlscript"
+ },
+ {
+ ".wmlsc",
+ "application/vnd.wap.wmlscriptc"
+ },
+ {
+ ".wmp",
+ "video/x-ms-wmp"
+ },
+ {
+ ".wmv",
+ "video/x-ms-wmv"
+ },
+ {
+ ".wmx",
+ "video/x-ms-wmx"
+ },
+ {
+ ".wmz",
+ "application/x-ms-wmz"
+ },
+ {
+ ".woff",
+ "application/font-woff"
+ },
+ {
+ ".woff2",
+ "font/woff2"
+ },
+ {
+ ".wps",
+ "application/vnd.ms-works"
+ },
+ {
+ ".wri",
+ "application/x-mswrite"
+ },
+ {
+ ".wrl",
+ "x-world/x-vrml"
+ },
+ {
+ ".wrz",
+ "x-world/x-vrml"
+ },
+ {
+ ".wsdl",
+ "text/xml"
+ },
+ {
+ ".wtv",
+ "video/x-ms-wtv"
+ },
+ {
+ ".wvx",
+ "video/x-ms-wvx"
+ },
+ {
+ ".x",
+ "application/directx"
+ },
+ {
+ ".xaf",
+ "x-world/x-vrml"
+ },
+ {
+ ".xaml",
+ "application/xaml+xml"
+ },
+ {
+ ".xap",
+ "application/x-silverlight-app"
+ },
+ {
+ ".xbap",
+ "application/x-ms-xbap"
+ },
+ {
+ ".xbm",
+ "image/x-xbitmap"
+ },
+ {
+ ".xdr",
+ "text/plain"
+ },
+ {
+ ".xht",
+ "application/xhtml+xml"
+ },
+ {
+ ".xhtml",
+ "application/xhtml+xml"
+ },
+ {
+ ".xla",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xlam",
+ "application/vnd.ms-excel.addin.macroEnabled.12"
+ },
+ {
+ ".xlc",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xlm",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xls",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xlsb",
+ "application/vnd.ms-excel.sheet.binary.macroEnabled.12"
+ },
+ {
+ ".xlsm",
+ "application/vnd.ms-excel.sheet.macroEnabled.12"
+ },
+ {
+ ".xlsx",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ },
+ {
+ ".xlt",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xltm",
+ "application/vnd.ms-excel.template.macroEnabled.12"
+ },
+ {
+ ".xltx",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template"
+ },
+ {
+ ".xlw",
+ "application/vnd.ms-excel"
+ },
+ {
+ ".xml",
+ "text/xml"
+ },
+ {
+ ".xof",
+ "x-world/x-vrml"
+ },
+ {
+ ".xpm",
+ "image/x-xpixmap"
+ },
+ {
+ ".xps",
+ "application/vnd.ms-xpsdocument"
+ },
+ {
+ ".xsd",
+ "text/xml"
+ },
+ {
+ ".xsf",
+ "text/xml"
+ },
+ {
+ ".xsl",
+ "text/xml"
+ },
+ {
+ ".xslt",
+ "text/xml"
+ },
+ {
+ ".xsn",
+ "application/octet-stream"
+ },
+ {
+ ".xtp",
+ "application/octet-stream"
+ },
+ {
+ ".xwd",
+ "image/x-xwindowdump"
+ },
+ {
+ ".z",
+ "application/x-compress"
+ },
+ {
+ ".zip",
+ "application/x-zip-compressed"
+ },
+ {
+ ".less",
+ "stylesheet/css"
+ }
+ })
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Provider/ModelBindingMetadataProvider.cs b/WaterCloud.Code/Provider/ModelBindingMetadataProvider.cs
new file mode 100644
index 0000000..096cbd6
--- /dev/null
+++ b/WaterCloud.Code/Provider/ModelBindingMetadataProvider.cs
@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// Controller Model Binding 处理
+ ///
+ public class ModelBindingMetadataProvider : IMetadataDetailsProvider, IDisplayMetadataProvider
+ {
+ public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
+ {
+ if (context.Key.MetadataKind == ModelMetadataKind.Property)
+ {
+ context.DisplayMetadata.ConvertEmptyStringToNull = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Security/DESEncrypt.cs b/WaterCloud.Code/Security/DESEncrypt.cs
new file mode 100644
index 0000000..630a28d
--- /dev/null
+++ b/WaterCloud.Code/Security/DESEncrypt.cs
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// DES加密、解密帮助类
+ ///
+ public class DESEncrypt
+ {
+ private static string DESKey = "WaterCloud_desencrypt_2019";
+
+ #region ========加密========
+
+ ///
+ /// 加密
+ ///
+ ///
+ ///
+ public static string Encrypt(string Text)
+ {
+ return Encrypt(Text, DESKey);
+ }
+
+ ///
+ /// 加密数据,用Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ ///
+ public static string Encrypt(string Text, string sKey)
+ {
+ DES des = DES.Create();
+ byte[] inputByteArray;
+ inputByteArray = Encoding.Default.GetBytes(Text);
+ des.Key = ASCIIEncoding.ASCII.GetBytes(DecryptMd5(sKey));
+ des.IV = ASCIIEncoding.ASCII.GetBytes(DecryptMd5(sKey));
+ System.IO.MemoryStream ms = new System.IO.MemoryStream();
+ CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
+ cs.Write(inputByteArray, 0, inputByteArray.Length);
+ cs.FlushFinalBlock();
+ StringBuilder ret = new StringBuilder();
+ foreach (byte b in ms.ToArray())
+ {
+ ret.AppendFormat("{0:X2}", b);
+ }
+ return ret.ToString();
+ }
+
+ ///
+ /// 加密数据, 用Security.MD5而非Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ public static string Encrypt2(string Text)
+ {
+ return Encrypt2(Text, DESKey);
+ }
+
+ ///
+ /// 加密数据, 用Security.MD5而非Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ ///
+ public static string Encrypt2(string Text, string sKey)
+ {
+ DES des = DES.Create();
+ byte[] inputByteArray;
+ inputByteArray = Encoding.Default.GetBytes(Text);
+ sKey = MD5(sKey).Substring(0, 8);
+ des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
+ des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
+ System.IO.MemoryStream ms = new System.IO.MemoryStream();
+ CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
+ cs.Write(inputByteArray, 0, inputByteArray.Length);
+ cs.FlushFinalBlock();
+ StringBuilder ret = new StringBuilder();
+ foreach (byte b in ms.ToArray())
+ {
+ ret.AppendFormat("{0:X2}", b);
+ }
+ return ret.ToString();
+ }
+
+ #endregion ========加密========
+
+ #region ========解密========
+
+ ///
+ /// 解密
+ ///
+ ///
+ ///
+ public static string Decrypt(string Text)
+ {
+ if (!string.IsNullOrEmpty(Text))
+ {
+ return Decrypt(Text, DESKey);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ ///
+ /// 解密数据,用Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ ///
+ public static string Decrypt(string Text, string sKey)
+ {
+ DES des = DES.Create();
+ int len;
+ len = Text.Length / 2;
+ byte[] inputByteArray = new byte[len];
+ int x, i;
+ for (x = 0; x < len; x++)
+ {
+ i = Convert.ToInt32(Text.Substring(x * 2, 2), 16);
+ inputByteArray[x] = (byte)i;
+ }
+
+ des.Key = ASCIIEncoding.ASCII.GetBytes(DecryptMd5(sKey));
+ des.IV = ASCIIEncoding.ASCII.GetBytes(DecryptMd5(sKey));
+ System.IO.MemoryStream ms = new System.IO.MemoryStream();
+ CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
+ cs.Write(inputByteArray, 0, inputByteArray.Length);
+ cs.FlushFinalBlock();
+ return Encoding.Default.GetString(ms.ToArray());
+ }
+
+ public static string DecryptMd5(string str)
+ {
+ string strEncrypt = string.Empty;
+ var md5 = System.Security.Cryptography.MD5.Create();
+ var data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
+ StringBuilder builder = new StringBuilder();
+ // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串
+ for (int i = 0; i < data.Length; i++)
+ {
+ builder.Append(data[i].ToString("X2"));
+ }
+ strEncrypt = builder.ToString().Substring(0, 8);
+ return strEncrypt;
+ }
+
+ ///
+ /// 解密数据,用Security.MD5而非Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ public static string Decrypt2(string Text)
+ {
+ if (!string.IsNullOrEmpty(Text))
+ {
+ return Decrypt2(Text, DESKey);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ ///
+ /// 解密数据,用Security.MD5而非Web.Security的Hash方式加密
+ ///
+ ///
+ ///
+ ///
+ public static string Decrypt2(string Text, string sKey)
+ {
+ DES des = DES.Create();
+ int len;
+ len = Text.Length / 2;
+ byte[] inputByteArray = new byte[len];
+ int x, i;
+ for (x = 0; x < len; x++)
+ {
+ i = Convert.ToInt32(Text.Substring(x * 2, 2), 16);
+ inputByteArray[x] = (byte)i;
+ }
+ sKey = MD5(sKey).Substring(0, 8);
+ des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
+ des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
+ System.IO.MemoryStream ms = new System.IO.MemoryStream();
+ CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
+ cs.Write(inputByteArray, 0, inputByteArray.Length);
+ cs.FlushFinalBlock();
+ return Encoding.Default.GetString(ms.ToArray());
+ }
+
+ #endregion ========解密========
+
+ public static string MD5(string pwd)
+ {
+ MD5 md5 = System.Security.Cryptography.MD5.Create();
+ byte[] data = System.Text.Encoding.Default.GetBytes(pwd);
+ byte[] md5data = md5.ComputeHash(data);
+ md5.Clear();
+ string str = "";
+ for (int i = 0; i < md5data.Length; i++)
+ {
+ str += md5data[i].ToString("x").PadLeft(2, '0');
+ }
+ return str;
+ }
+
+ ///
+ /// 基于Sha1的自定义加密字符串方法:输入一个字符串,返回一个由40个字符组成的十六进制的哈希散列(字符串)。
+ ///
+ /// 要加密的字符串
+ /// 加密后的十六进制的哈希散列(字符串)
+ public static string Sha1(string str)
+ {
+ var buffer = Encoding.UTF8.GetBytes(str);
+ var data = SHA1.Create().ComputeHash(buffer);
+
+ var sb = new StringBuilder();
+ foreach (var t in data)
+ {
+ sb.Append(t.ToString("X2"));
+ }
+
+ return sb.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Security/Md5.cs b/WaterCloud.Code/Security/Md5.cs
new file mode 100644
index 0000000..af3925a
--- /dev/null
+++ b/WaterCloud.Code/Security/Md5.cs
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright © 2016 WaterCloud.Framework 版权所有
+ * Author: WaterCloud
+ * Description: WaterCloud快速开发平台
+ * Website:
+*********************************************************************************/
+
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace WaterCloud.Code
+{
+ ///
+ /// MD5加密
+ ///
+ public class Md5
+ {
+ ///
+ /// MD5加密
+ ///
+ /// 加密字符
+ /// 加密位数16/32
+ ///
+ public static string md5(string str, int code)
+ {
+ string strEncrypt = string.Empty;
+ if (code == 16)
+ {
+ var md5 = System.Security.Cryptography.MD5.Create();
+ var data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
+ StringBuilder builder = new StringBuilder();
+ // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串
+ for (int i = 0; i < data.Length; i++)
+ {
+ builder.Append(data[i].ToString("X2"));
+ }
+ strEncrypt = builder.ToString().Substring(8, 16);
+ }
+
+ if (code == 32)
+ {
+ var md5 = System.Security.Cryptography.MD5.Create();
+ var data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
+ StringBuilder builder = new StringBuilder();
+ // 循环遍历哈希数据的每一个字节并格式化为十六进制字符串
+ for (int i = 0; i < data.Length; i++)
+ {
+ builder.Append(data[i].ToString("X2"));
+ }
+ strEncrypt = builder.ToString();
+ }
+
+ return strEncrypt;
+ }
+
+ public static string MD5(string str)
+ {
+ MD5 md5 = System.Security.Cryptography.MD5.Create();
+ byte[] data = System.Text.Encoding.Default.GetBytes(str);
+ byte[] md5data = md5.ComputeHash(data);
+ md5.Clear();
+ str = "";
+ for (int i = 0; i < md5data.Length; i++)
+ {
+ str += md5data[i].ToString("x").PadLeft(2, '0');
+ }
+ return str;
+ }
+
+ public static string MD5Lower16(string str)
+ {
+ return MD5(str).ToLower().Substring(8, 16);
+ }
+
+ public static string MD5Lower32(string str)
+ {
+ return MD5(str).ToLower(); ;
+ }
+
+ ///
+ /// 32位小写
+ ///
+ ///
+ public static string SHA1(string s)
+ {
+ SHA1 sha1 = System.Security.Cryptography.SHA1.Create();
+ byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(s));
+ string shaStr = BitConverter.ToString(hash);
+ shaStr = shaStr.Replace("-", "");
+ shaStr = shaStr.ToLower();
+ return s.ToLower();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/AsyncTaskHelper.cs b/WaterCloud.Code/Util/AsyncTaskHelper.cs
new file mode 100644
index 0000000..cf9bd92
--- /dev/null
+++ b/WaterCloud.Code/Util/AsyncTaskHelper.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ public class AsyncTaskHelper
+ {
+ ///
+ /// 开始异步任务
+ ///
+ ///
+ public static void StartTask(Action action)
+ {
+ try
+ {
+ Action newAction = () =>
+ { };
+ newAction += action;
+ Task task = new Task(newAction);
+ task.Start();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteWithTime(ex);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/CommonEnum.cs b/WaterCloud.Code/Util/CommonEnum.cs
new file mode 100644
index 0000000..40b84a1
--- /dev/null
+++ b/WaterCloud.Code/Util/CommonEnum.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace WaterCloud.Code
+{
+ public enum StatusEnum
+ {
+ [Description("启用")]
+ Yes = 1,
+
+ [Description("禁用")]
+ No = 0
+ }
+
+ public enum IsEnum
+ {
+ [Description("是")]
+ Yes = 1,
+
+ [Description("否")]
+ No = 0
+ }
+
+ public enum NeedEnum
+ {
+ [Description("不需要")]
+ NotNeed = 0,
+
+ [Description("需要")]
+ Need = 1
+ }
+
+ public enum OperateStatusEnum
+ {
+ [Description("失败")]
+ Fail = 0,
+
+ [Description("成功")]
+ Success = 1
+ }
+
+ public enum UploadFileType
+ {
+ [Description("头像")]
+ Portrait = 1,
+
+ [Description("新闻图片")]
+ News = 2,
+
+ [Description("导入的文件")]
+ Import = 10
+ }
+
+ public enum PlatformEnum
+ {
+ [Description("Web后台")]
+ Web = 1,
+
+ [Description("WebApi")]
+ WebApi = 2
+ }
+
+ public enum PayStatusEnum
+ {
+ [Description("未知")]
+ Unknown = 0,
+
+ [Description("已支付")]
+ Success = 1,
+
+ [Description("转入退款")]
+ Refund = 2,
+
+ [Description("未支付")]
+ NotPay = 3,
+
+ [Description("已关闭")]
+ Closed = 4,
+
+ [Description("已撤销(付款码支付)")]
+ Revoked = 5,
+
+ [Description("用户支付中(付款码支付)")]
+ UserPaying = 6,
+
+ [Description("支付失败(其他原因,如银行返回失败)")]
+ PayError = 7
+ }
+
+ public class EnumHelper
+ {
+ ///
+ /// 获取枚举列表
+ ///
+ ///
+ ///
+ public static Dictionary EnumToDic()
+ {
+ Dictionary list = new Dictionary();
+ foreach (var e in Enum.GetValues(typeof(T)))
+ {
+ list.Add(Convert.ToInt32(e), e.GetDescriptionByEnum());
+ }
+ return list;
+ }
+
+ ///
+ /// 获取枚举列表
+ ///
+ ///
+ ///
+ public static List EnumToList()
+ {
+ List list = new List();
+
+ foreach (var e in Enum.GetValues(typeof(T)))
+ {
+ list.Add(e.GetDescriptionByEnum());
+ }
+ return list;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/ComputerHelper.cs b/WaterCloud.Code/Util/ComputerHelper.cs
new file mode 100644
index 0000000..37acf5e
--- /dev/null
+++ b/WaterCloud.Code/Util/ComputerHelper.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace WaterCloud.Code
+{
+ public class ComputerHelper
+ {
+ public static ComputerInfo GetComputerInfo()
+ {
+ ComputerInfo computerInfo = new ComputerInfo();
+ try
+ {
+ MemoryMetricsClient client = new MemoryMetricsClient();
+ MemoryMetrics memoryMetrics = client.GetMetrics();
+ computerInfo.TotalRAM = Math.Ceiling(memoryMetrics.Total / 1024).ToString() + " GB";
+ computerInfo.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString();
+ computerInfo.CPURate = Math.Ceiling(GetCPURate().ToDouble()).ToString();
+ computerInfo.RunTime = GetRunTime();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteWithTime(ex);
+ }
+ return computerInfo;
+ }
+
+ public static bool IsUnix()
+ {
+ var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ return isUnix;
+ }
+
+ public static string GetCPURate()
+ {
+ string cpuRate = string.Empty;
+ if (IsUnix())
+ {
+ string output = ShellHelper.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'");
+ cpuRate = output.Trim();
+ }
+ else
+ {
+ string output = ShellHelper.Cmd("wmic", "cpu get LoadPercentage");
+ cpuRate = output.Replace("LoadPercentage", string.Empty).Trim();
+ }
+ return cpuRate;
+ }
+
+ public static string GetRunTime()
+ {
+ string runTime = string.Empty;
+ try
+ {
+ if (IsUnix())
+ {
+ string output = ShellHelper.Bash("uptime -s");
+ output = output.Trim();
+ runTime = Extensions.FormatTime((DateTime.Now - output.ToDate()).TotalMilliseconds.ToString().Split('.')[0].ToLong());
+ }
+ else
+ {
+ string output = ShellHelper.Cmd("wmic", "OS get LastBootUpTime/Value");
+ string[] outputArr = output.Split("=", StringSplitOptions.RemoveEmptyEntries);
+ if (outputArr.Length == 2)
+ {
+ runTime = Extensions.FormatTime((DateTime.Now - outputArr[1].Split('.')[0].ToDate()).TotalMilliseconds.ToString().Split('.')[0].ToLong());
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteWithTime(ex);
+ }
+ return runTime;
+ }
+ }
+
+ public class MemoryMetrics
+ {
+ public double Total { get; set; }
+ public double Used { get; set; }
+ public double Free { get; set; }
+ }
+
+ public class MemoryMetricsClient
+ {
+ public MemoryMetrics GetMetrics()
+ {
+ if (ComputerHelper.IsUnix())
+ {
+ return GetUnixMetrics();
+ }
+ return GetWindowsMetrics();
+ }
+
+ private MemoryMetrics GetWindowsMetrics()
+ {
+ string output = ShellHelper.Cmd("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value");
+
+ var lines = output.Trim().Split("\n");
+ var freeMemoryParts = lines[0].Split("=", StringSplitOptions.RemoveEmptyEntries);
+ var totalMemoryParts = lines[1].Split("=", StringSplitOptions.RemoveEmptyEntries);
+
+ var metrics = new MemoryMetrics();
+ metrics.Total = Math.Round(double.Parse(totalMemoryParts[1]) / 1024, 0);
+ metrics.Free = Math.Round(double.Parse(freeMemoryParts[1]) / 1024, 0);
+ metrics.Used = metrics.Total - metrics.Free;
+
+ return metrics;
+ }
+
+ private MemoryMetrics GetUnixMetrics()
+ {
+ string output = ShellHelper.Bash("free -m");
+
+ var lines = output.Split("\n");
+ var memory = lines[1].Split(" ", StringSplitOptions.RemoveEmptyEntries);
+
+ var metrics = new MemoryMetrics();
+ metrics.Total = double.Parse(memory[1]);
+ metrics.Used = double.Parse(memory[2]);
+ metrics.Free = double.Parse(memory[3]);
+
+ return metrics;
+ }
+ }
+
+ public class ComputerInfo
+ {
+ ///
+ /// CPU使用率
+ ///
+ public string CPURate { get; set; }
+
+ ///
+ /// 总内存
+ ///
+ public string TotalRAM { get; set; }
+
+ ///
+ /// 内存使用率
+ ///
+ public string RAMRate { get; set; }
+
+ ///
+ /// 系统运行时间
+ ///
+ public string RunTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/ConcurrentList.cs b/WaterCloud.Code/Util/ConcurrentList.cs
new file mode 100644
index 0000000..e32b660
--- /dev/null
+++ b/WaterCloud.Code/Util/ConcurrentList.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+
+namespace WaterCloud.Code
+{
+ public class ConcurrentList : IList
+ {
+ protected static object _lock = new object();
+ protected List _interalList = new List();
+
+ public IEnumerator GetEnumerator()
+ {
+ return Clone().GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return Clone().GetEnumerator();
+ }
+
+ public int Count
+ { get { return _interalList.Count; } }
+
+ public bool IsReadOnly
+ { get { return false; } }
+
+ public T this[int index]
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _interalList[index];
+ }
+ }
+ set
+ {
+ lock (_lock)
+ {
+ _interalList[index] = value;
+ }
+ }
+ }
+
+ public List Clone()
+ {
+ List newList = new List();
+ lock (_lock)
+ {
+ _interalList.ForEach(x => newList.Add(x));
+ }
+ return newList;
+ }
+
+ public int IndexOf(T item)
+ {
+ return _interalList.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ _interalList.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ lock (_lock)
+ {
+ _interalList.RemoveAt(index);
+ }
+ }
+
+ public void Add(T item)
+ {
+ lock (_lock)
+ {
+ _interalList.Add(item);
+ }
+ }
+
+ public void AddRange(IEnumerable list)
+ {
+ foreach (T item in list)
+ {
+ Add(item);
+ }
+ }
+
+ public void Clear()
+ {
+ lock (_lock)
+ {
+ _interalList.Clear();
+ }
+ }
+
+ public bool Contains(T item)
+ {
+ return _interalList.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _interalList.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(T item)
+ {
+ if (item == null)
+ {
+ return false;
+ }
+ lock (_lock)
+ {
+ return _interalList.Remove(item);
+ }
+ }
+
+ public void RemoveAll(Predicate match)
+ {
+ if (match == null)
+ {
+ return;
+ }
+ Contract.Ensures(Contract.Result() >= 0);
+ Contract.Ensures(Contract.Result() <= Contract.OldValue(Count));
+ Contract.EndContractBlock();
+
+ foreach (T t in Clone())
+ {
+ if (match(t))
+ {
+ Remove(t);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/DataTableHelper.cs b/WaterCloud.Code/Util/DataTableHelper.cs
new file mode 100644
index 0000000..4fd42c8
--- /dev/null
+++ b/WaterCloud.Code/Util/DataTableHelper.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Reflection;
+
+namespace WaterCloud.Code
+{
+ public static class DataTableHelper
+ {
+ public static DataTable ListToDataTable(List entitys)
+ {
+ //检查实体集合不能为空
+ if (entitys == null || entitys.Count < 1)
+ {
+ throw new Exception("需转换的集合为空");
+ }
+ //取出第一个实体的所有Propertie
+ Type entityType = entitys[0].GetType();
+ PropertyInfo[] entityProperties = entityType.GetProperties();
+
+ //生成DataTable的structure
+ //生产代码中,应将生成的DataTable结构Cache起来,此处略
+ DataTable dt = new DataTable();
+ for (int i = 0; i < entityProperties.Length; i++)
+ {
+ dt.Columns.Add(entityProperties[i].Name);
+ }
+ //将所有entity添加到DataTable中
+ foreach (object entity in entitys)
+ {
+ //检查所有的的实体都为同一类型
+ if (entity.GetType() != entityType)
+ {
+ throw new Exception("要转换的集合元素类型不一致");
+ }
+ object[] entityValues = new object[entityProperties.Length];
+ for (int i = 0; i < entityProperties.Length; i++)
+ {
+ entityValues[i] = entityProperties[i].GetValue(entity, null);
+ }
+ dt.Rows.Add(entityValues);
+ }
+ return dt;
+ }
+
+ ///
+ /// List过滤
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static List ListFilter(List entitys, List list)
+ {
+ //检查实体集合不能为空
+ if (entitys == null || entitys.Count < 1)
+ {
+ throw new Exception("需转换的集合为空");
+ }
+ //取出第一个实体的所有Propertie
+ Type entityType = entitys[0].GetType();
+ PropertyInfo[] entityProperties = entityType.GetProperties();
+
+ //将所有entity过滤
+ foreach (object entity in entitys)
+ {
+ //检查所有的的实体都为同一类型
+ if (entity.GetType() != entityType)
+ {
+ throw new Exception("要转换的集合元素类型不一致");
+ }
+ for (int i = 0; i < entityProperties.Length; i++)
+ {
+ if (!list.Contains(entityProperties[i].Name))
+ {
+ entityProperties[i].SetValue(entity, null);
+ }
+ }
+ }
+ return entitys;
+ }
+
+ ///
+ /// DataTable转成List
+ ///
+ ///
+ ///
+ ///
+ public static List ToDataList(this DataTable dt)
+ {
+ var list = new List();
+ var plist = new List(typeof(T).GetProperties());
+ foreach (DataRow item in dt.Rows)
+ {
+ T s = Activator.CreateInstance();
+ for (int i = 0; i < dt.Columns.Count; i++)
+ {
+ PropertyInfo info = plist.Find(p => p.Name == dt.Columns[i].ColumnName);
+ if (info != null)
+ {
+ try
+ {
+ if (!Convert.IsDBNull(item[i]))
+ {
+ object v = null;
+ if (info.PropertyType.ToString().Contains("System.Nullable"))
+ {
+ v = Convert.ChangeType(item[i], Nullable.GetUnderlyingType(info.PropertyType));
+ }
+ else
+ {
+ v = Convert.ChangeType(item[i], info.PropertyType);
+ }
+ info.SetValue(s, v, null);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("字段[" + info.Name + "]转换出错," + ex.Message);
+ }
+ }
+ }
+ list.Add(s);
+ }
+ return list;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/FileHelper.cs b/WaterCloud.Code/Util/FileHelper.cs
new file mode 100644
index 0000000..653c224
--- /dev/null
+++ b/WaterCloud.Code/Util/FileHelper.cs
@@ -0,0 +1,697 @@
+using System;
+using System.Data;
+using System.IO;
+using System.Text;
+
+namespace WaterCloud.Code
+{
+ public class FileHelper
+ {
+ public static string MapPath(string path)
+ {
+ try
+ {
+ string rootdir = Directory.GetCurrentDirectory();
+ //DirectoryInfo Dir = Directory.GetParent(rootdir);
+ //string root = Dir.Parent.Parent.Parent.FullName;
+ return rootdir + path;
+ }
+ catch (Exception)
+ {
+ return path;
+ }
+ }
+
+ #region 获取文件到集合中
+
+ ///
+ /// 读取指定位置文件列表到集合中
+ ///
+ /// 指定路径
+ ///
+ public static DataTable GetFileTable(string path)
+ {
+ DataTable dt = new DataTable();
+ dt.Columns.Add("name", typeof(string));
+ dt.Columns.Add("ext", typeof(string));
+ dt.Columns.Add("size", typeof(long));
+ dt.Columns.Add("time", typeof(DateTime));
+
+ if (Directory.Exists(path))
+ {
+ DirectoryInfo dirinfo = new DirectoryInfo(path);
+ FileInfo fi;
+ DirectoryInfo dir;
+ string FileName, FileExt;
+ long FileSize = 0;
+ DateTime FileModify;
+ try
+ {
+ foreach (FileSystemInfo fsi in dirinfo.GetFileSystemInfos())
+ {
+ FileName = string.Empty;
+ FileExt = string.Empty;
+ if (fsi is FileInfo)
+ {
+ fi = (FileInfo)fsi;
+ //获取文件名称
+ FileName = fi.Name;
+ //获取文件扩展名
+ FileExt = fi.Extension;
+ //获取文件大小
+ FileSize = fi.Length;
+ //获取文件最后修改时间
+ FileModify = fi.LastWriteTime;
+ }
+ else
+ {
+ dir = (DirectoryInfo)fsi;
+ //获取目录名
+ FileName = dir.Name;
+ //获取目录最后修改时间
+ FileModify = dir.LastWriteTime;
+ //设置目录文件为文件夹
+ FileExt = "文件夹";
+ }
+ DataRow dr = dt.NewRow();
+ dr["name"] = FileName;
+ dr["ext"] = FileExt;
+ dr["size"] = FileSize;
+ dr["time"] = FileModify;
+ dt.Rows.Add(dr);
+ }
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ return dt;
+ }
+
+ #endregion 获取文件到集合中
+
+ #region 检测指定路径是否存在
+
+ ///
+ /// 检测指定路径是否存在
+ ///
+ /// 目录的绝对路径
+ public static bool IsExistDirectory(string path)
+ {
+ return Directory.Exists(path);
+ }
+
+ #endregion 检测指定路径是否存在
+
+ #region 检测指定文件是否存在,如果存在则返回true
+
+ ///
+ /// 检测指定文件是否存在,如果存在则返回true
+ ///
+ /// 文件的绝对路径
+ public static bool IsExistFile(string filePath)
+ {
+ return File.Exists(filePath);
+ }
+
+ #endregion 检测指定文件是否存在,如果存在则返回true
+
+ #region 创建文件夹
+
+ ///
+ /// 创建文件夹
+ ///
+ /// 文件夹的绝对路径
+ public static void CreateFolder(string folderPath)
+ {
+ if (!IsExistDirectory(folderPath))
+ {
+ Directory.CreateDirectory(folderPath);
+ }
+ }
+
+ #endregion 创建文件夹
+
+ #region 判断上传文件后缀名
+
+ ///
+ /// 判断上传文件后缀名
+ ///
+ /// 后缀名
+ public static bool IsCanEdit(string strExtension)
+ {
+ strExtension = strExtension.ToLower();
+ if (strExtension.LastIndexOf(".", StringComparison.Ordinal) >= 0)
+ {
+ strExtension = strExtension.Substring(strExtension.LastIndexOf(".", StringComparison.Ordinal));
+ }
+ else
+ {
+ strExtension = ".txt";
+ }
+ string[] strArray = new string[] { ".htm", ".html", ".txt", ".js", ".css", ".xml", ".sitemap" };
+ for (int i = 0; i < strArray.Length; i++)
+ {
+ if (strExtension.Equals(strArray[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool IsSafeName(string strExtension)
+ {
+ strExtension = strExtension.ToLower();
+ if (strExtension.LastIndexOf(".") >= 0)
+ {
+ strExtension = strExtension.Substring(strExtension.LastIndexOf("."));
+ }
+ else
+ {
+ strExtension = ".txt";
+ }
+ string[] strArray = new string[] { ".jpg", ".gif", ".png" };
+ for (int i = 0; i < strArray.Length; i++)
+ {
+ if (strExtension.Equals(strArray[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool IsZipName(string strExtension)
+ {
+ strExtension = strExtension.ToLower();
+ if (strExtension.LastIndexOf(".") >= 0)
+ {
+ strExtension = strExtension.Substring(strExtension.LastIndexOf("."));
+ }
+ else
+ {
+ strExtension = ".txt";
+ }
+ string[] strArray = new string[] { ".zip", ".rar" };
+ for (int i = 0; i < strArray.Length; i++)
+ {
+ if (strExtension.Equals(strArray[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ #endregion 判断上传文件后缀名
+
+ #region 创建文件夹
+
+ ///
+ /// 创建文件夹
+ ///
+ /// 文件的绝对路径
+ public static void CreateSuffic(string fileName)
+ {
+ try
+ {
+ if (!Directory.Exists(fileName))
+ {
+ Directory.CreateDirectory(fileName);
+ }
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// 创建文件夹
+ ///
+ /// 文件的绝对路径
+ public static void CreateFiles(string fileName)
+ {
+ try
+ {
+ //判断文件是否存在,不存在创建该文件
+ if (!IsExistFile(fileName))
+ {
+ FileInfo file = new FileInfo(fileName);
+ FileStream fs = file.Create();
+ fs.Close();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteWithTime(ex);
+ }
+ }
+
+ #region 创建文本文件
+
+ ///
+ /// 创建文件
+ ///
+ ///
+ ///
+ public static void CreateFile(string path, string content)
+ {
+ if (!Directory.Exists(Path.GetDirectoryName(path)))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ }
+ using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8))
+ {
+ sw.Write(content);
+ }
+ }
+
+ #endregion 创建文本文件
+
+ ///
+ /// 创建一个文件,并将字节流写入文件。
+ ///
+ /// 文件的绝对路径
+ /// 二进制流数据
+ public static void CreateFile(string filePath, byte[] buffer)
+ {
+ try
+ {
+ //判断文件是否存在,不存在创建该文件
+ if (!IsExistFile(filePath))
+ {
+ FileInfo file = new FileInfo(filePath);
+ FileStream fs = file.Create();
+ fs.Write(buffer, 0, buffer.Length);
+ fs.Close();
+ }
+ else
+ {
+ File.WriteAllBytes(filePath, buffer);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteWithTime(ex);
+ }
+ }
+
+ #endregion 创建文件夹
+
+ #region 将文件移动到指定目录
+
+ ///
+ /// 将文件移动到指定目录
+ ///
+ /// 需要移动的源文件的绝对路径
+ /// 移动到的目录的绝对路径
+ public static void Move(string sourceFilePath, string descDirectoryPath)
+ {
+ string sourceName = GetFileName(sourceFilePath);
+ if (IsExistDirectory(descDirectoryPath))
+ {
+ //如果目标中存在同名文件,则删除
+ if (IsExistFile(descDirectoryPath + "\\" + sourceFilePath))
+ {
+ DeleteFile(descDirectoryPath + "\\" + sourceFilePath);
+ }
+ else
+ {
+ //将文件移动到指定目录
+ File.Move(sourceFilePath, descDirectoryPath + "\\" + sourceFilePath);
+ }
+ }
+ }
+
+ #endregion 将文件移动到指定目录
+
+ #region 将源文件的内容复制到目标文件中
+
+ ///
+ /// 将源文件的内容复制到目标文件中
+ ///
+ /// 源文件的绝对路径
+ /// 目标文件的绝对路径
+ public static void Copy(string sourceFilePath, string descDirectoryPath)
+ {
+ File.Copy(sourceFilePath, descDirectoryPath, true);
+ }
+
+ #endregion 将源文件的内容复制到目标文件中
+
+ #region 从文件的绝对路径中获取文件名( 不包含扩展名 )
+
+ ///
+ /// 从文件的绝对路径中获取文件名( 不包含扩展名 )
+ ///
+ /// 文件的绝对路径
+ public static string GetFileName(string filePath)
+ {
+ FileInfo file = new FileInfo(filePath);
+ return file.Name;
+ }
+
+ #endregion 从文件的绝对路径中获取文件名( 不包含扩展名 )
+
+ #region 获取文件的后缀名
+
+ ///
+ /// 获取文件的后缀名
+ ///
+ /// 文件的绝对路径
+ public static string GetExtension(string filePath)
+ {
+ FileInfo file = new FileInfo(filePath);
+ return file.Extension;
+ }
+
+ ///
+ /// 返回文件扩展名,不含“.”
+ ///
+ /// 文件全名称
+ /// string
+ public static string GetFileExt(string filepath)
+ {
+ if (string.IsNullOrEmpty(filepath))
+ {
+ return "";
+ }
+ if (filepath.LastIndexOf(".", StringComparison.Ordinal) > 0)
+ {
+ return filepath.Substring(filepath.LastIndexOf(".", StringComparison.Ordinal) + 1); //文件扩展名,不含“.”
+ }
+ return "";
+ }
+
+ #endregion 获取文件的后缀名
+
+ #region 删除指定文件
+
+ ///
+ /// 删除指定文件
+ ///
+ /// 文件的绝对路径
+ public static void DeleteFile(string filePath)
+ {
+ if (IsExistFile(filePath))
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ #endregion 删除指定文件
+
+ #region 删除指定目录及其所有子目录
+
+ ///
+ /// 删除指定目录及其所有子目录
+ ///
+ /// 文件的绝对路径
+ public static void DeleteDirectory(string directoryPath)
+ {
+ if (IsExistDirectory(directoryPath))
+ {
+ Directory.Delete(directoryPath);
+ }
+ }
+
+ #endregion 删除指定目录及其所有子目录
+
+ #region 清空指定目录下所有文件及子目录,但该目录依然保存.
+
+ ///
+ /// 清空指定目录下所有文件及子目录,但该目录依然保存.
+ ///
+ /// 指定目录的绝对路径
+ public static void ClearDirectory(string directoryPath)
+ {
+ if (!IsExistDirectory(directoryPath)) return;
+ //删除目录中所有的文件
+ string[] fileNames = GetFileNames(directoryPath);
+ for (int i = 0; i < fileNames.Length; i++)
+ {
+ DeleteFile(fileNames[i]);
+ }
+ //删除目录中所有的子目录
+ string[] directoryNames = GetDirectories(directoryPath);
+ for (int i = 0; i < directoryNames.Length; i++)
+ {
+ DeleteDirectory(directoryNames[i]);
+ }
+ }
+
+ #endregion 清空指定目录下所有文件及子目录,但该目录依然保存.
+
+ #region 剪切 粘贴
+
+ ///
+ /// 剪切文件
+ ///
+ /// 原路径
+ /// 新路径
+ public bool FileMove(string source, string destination)
+ {
+ bool ret = false;
+ FileInfo file_s = new FileInfo(source);
+ FileInfo file_d = new FileInfo(destination);
+ if (file_s.Exists)
+ {
+ if (!file_d.Exists)
+ {
+ file_s.MoveTo(destination);
+ ret = true;
+ }
+ }
+ if (ret == true)
+ {
+ //Response.Write("");
+ }
+ else
+ {
+ //Response.Write("");
+ }
+ return ret;
+ }
+
+ #endregion 剪切 粘贴
+
+ #region 检测指定目录是否为空
+
+ ///
+ /// 检测指定目录是否为空
+ ///
+ /// 指定目录的绝对路径
+ public static bool IsEmptyDirectory(string directoryPath)
+ {
+ try
+ {
+ //判断文件是否存在
+ string[] fileNames = GetFileNames(directoryPath);
+ if (fileNames.Length > 0)
+ {
+ return false;
+ }
+ //判断是否存在文件夹
+ string[] directoryNames = GetDirectories(directoryPath);
+ if (directoryNames.Length > 0)
+ {
+ return false;
+ }
+ return true;
+ }
+ catch (Exception)
+ {
+ return true;
+ }
+ }
+
+ #endregion 检测指定目录是否为空
+
+ #region 获取指定目录中所有文件列表
+
+ ///
+ /// 获取指定目录中所有文件列表
+ ///
+ /// 指定目录的绝对路径
+ public static string[] GetFileNames(string directoryPath)
+ {
+ if (!IsExistDirectory(directoryPath))
+ {
+ throw new FileNotFoundException();
+ }
+ return Directory.GetFiles(directoryPath);
+ }
+
+ #endregion 获取指定目录中所有文件列表
+
+ #region 获取指定目录中的子目录列表
+
+ ///
+ /// 获取指定目录中所有子目录列表,若要搜索嵌套的子目录列表,请使用重载方法
+ ///
+ /// 指定目录的绝对路径
+ public static string[] GetDirectories(string directoryPath)
+ {
+ return Directory.GetDirectories(directoryPath);
+ }
+
+ ///
+ /// 获取指定目录及子目录中所有子目录列表
+ ///
+ /// 指定目录的绝对路径
+ /// 模式字符串,"*"代表0或N个字符,"?"代表1个字符。
+ /// 范例:"Log*.xml"表示搜索所有以Log开头的Xml文件。
+ /// 是否搜索子目录
+ public static string[] GetDirectories(string directoryPath, string searchPattern, bool isSearchChild)
+ {
+ if (isSearchChild)
+ {
+ return Directory.GetDirectories(directoryPath, searchPattern, SearchOption.AllDirectories);
+ }
+ else
+ {
+ return Directory.GetDirectories(directoryPath, searchPattern, SearchOption.TopDirectoryOnly);
+ }
+ }
+
+ #endregion 获取指定目录中的子目录列表
+
+ #region 获取一个文件的长度
+
+ ///
+ /// 获取一个文件的长度,单位为Byte
+ ///
+ /// 文件的绝对路径
+ public static int GetFileSize(string filePath)
+ {
+ //创建一个文件对象
+ FileInfo fi = new FileInfo(filePath);
+ //获取文件的大小
+ return (int)fi.Length;
+ }
+
+ ///
+ /// 获取一个文件的长度,单位为KB
+ ///
+ /// 文件的路径
+ public static double GetFileSizeByKb(string filePath)
+ {
+ //创建一个文件对象
+ FileInfo fi = new FileInfo(filePath);
+ //获取文件的大小
+ return Math.Round(Convert.ToDouble(filePath.Length) / 1024, 2);// ConvertHelper.ToDouble(ConvertHelper.ToDouble(fi.Length) / 1024, 1);
+ }
+
+ ///
+ /// 获取一个文件的长度,单位为MB
+ ///
+ /// 文件的路径
+ public static double GetFileSizeByMb(string filePath)
+ {
+ //创建一个文件对象
+ FileInfo fi = new FileInfo(filePath);
+ //获取文件的大小
+ return Math.Round(Convert.ToDouble(Convert.ToDouble(fi.Length) / 1024 / 1024), 2);
+ }
+
+ #endregion 获取一个文件的长度
+
+ #region 获取文件大小并以B,KB,GB,TB
+
+ ///
+ /// 计算文件大小函数(保留两位小数),Size为字节大小
+ ///
+ /// 初始文件大小
+ ///
+ public static string ToFileSize(long size)
+ {
+ string m_strSize = "";
+ long FactSize = 0;
+ FactSize = size;
+ if (FactSize < 1024.00)
+ m_strSize = FactSize.ToString("F2") + " 字节";
+ else if (FactSize >= 1024.00 && FactSize < 1048576)
+ m_strSize = (FactSize / 1024.00).ToString("F2") + " KB";
+ else if (FactSize >= 1048576 && FactSize < 1073741824)
+ m_strSize = (FactSize / 1024.00 / 1024.00).ToString("F2") + " MB";
+ else if (FactSize >= 1073741824)
+ m_strSize = (FactSize / 1024.00 / 1024.00 / 1024.00).ToString("F2") + " GB";
+ return m_strSize;
+ }
+
+ #endregion 获取文件大小并以B,KB,GB,TB
+
+ #region 将文件读取到字符串中
+
+ ///
+ /// 将文件读取到字符串中
+ ///
+ /// 文件的绝对路径
+ public static string FileToString(string filePath)
+ {
+ return FileToString(filePath, Encoding.UTF8);
+ }
+
+ ///
+ /// 将文件读取到字符串中
+ ///
+ /// 文件的绝对路径
+ /// 字符编码
+ public static string FileToString(string filePath, Encoding encoding)
+ {
+ //创建流读取器
+ StreamReader reader = new StreamReader(filePath, encoding);
+ try
+ {
+ //读取流
+ return reader.ReadToEnd();
+ }
+ finally
+ {
+ //关闭流读取器
+ reader.Close();
+ }
+ }
+
+ #endregion 将文件读取到字符串中
+
+ #region 判断文件
+
+ // 判断文件是否是bai图片
+ public static bool IsPicture(string fileName)
+ {
+ string strFilter = ".jpeg|.gif|.jpg|.png|.bmp|.pic|.tiff|.ico|.iff|.lbm|.mag|.mac|.mpt|.opt|";
+ char[] separtor = { '|' };
+ string[] tempFileds = StringSplit(strFilter, separtor);
+ foreach (string str in tempFileds)
+ {
+ if (str.ToUpper() == fileName.Substring(fileName.LastIndexOf("."), fileName.Length - fileName.LastIndexOf(".")).ToUpper()) { return true; }
+ }
+ return false;
+ }
+
+ // 判断文件是否是excle
+ public static bool IsExcel(string fileName)
+ {
+ string strFilter = ".xls|.xlsx|";
+ char[] separtor = { '|' };
+ string[] tempFileds = StringSplit(strFilter, separtor);
+ foreach (string str in tempFileds)
+ {
+ if (str.ToUpper() == fileName.Substring(fileName.LastIndexOf("."), fileName.Length - fileName.LastIndexOf(".")).ToUpper()) { return true; }
+ }
+ return false;
+ }
+
+ // 通过字符串,分隔符返回zhistring[]数组
+ public static string[] StringSplit(string s, char[] separtor)
+ {
+ string[] tempFileds = s.Trim().Split(separtor); return tempFileds;
+ }
+
+ #endregion 判断文件
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/HttpWebClient.cs b/WaterCloud.Code/Util/HttpWebClient.cs
new file mode 100644
index 0000000..08c58f2
--- /dev/null
+++ b/WaterCloud.Code/Util/HttpWebClient.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+
+namespace WaterCloud.Code
+{
+ public class HttpWebClient
+ {
+ private IHttpClientFactory _httpClientFactory;
+
+ public HttpWebClient(IHttpClientFactory httpClientFactory)
+ {
+ this._httpClientFactory = httpClientFactory;
+ }
+
+ ///
+ /// Get异步请求
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task GetAsync(string url, Dictionary dicHeaders, int timeoutSecond = 120)
+ {
+ var client = _httpClientFactory.CreateClient();
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
+ if (dicHeaders != null)
+ {
+ foreach (var header in dicHeaders)
+ {
+ request.Headers.Add(header.Key, header.Value);
+ }
+ }
+ client.Timeout = TimeSpan.FromSeconds(timeoutSecond);
+ var response = await client.SendAsync(request);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task PostAsync(string url, string requestString, Dictionary dicHeaders, int timeoutSecond)
+ {
+ var client = _httpClientFactory.CreateClient();
+ var requestContent = new StringContent(requestString);
+ if (dicHeaders != null)
+ {
+ foreach (var head in dicHeaders)
+ {
+ requestContent.Headers.Add(head.Key, head.Value);
+ }
+ }
+ client.Timeout = TimeSpan.FromSeconds(timeoutSecond);
+ var response = await client.PostAsync(url, requestContent);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task PutAsync(string url, string requestString, Dictionary dicHeaders, int timeoutSecond)
+ {
+ var client = _httpClientFactory.CreateClient();
+ var requestContent = new StringContent(requestString);
+ if (dicHeaders != null)
+ {
+ foreach (var head in dicHeaders)
+ {
+ requestContent.Headers.Add(head.Key, head.Value);
+ }
+ }
+ client.Timeout = TimeSpan.FromSeconds(timeoutSecond);
+ var response = await client.PutAsync(url, requestContent);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+
+ ///
+ /// Patch异步请求
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task PatchAsync(string url, string requestString, Dictionary dicHeaders, int timeoutSecond)
+ {
+ var client = _httpClientFactory.CreateClient();
+ var requestContent = new StringContent(requestString);
+ if (dicHeaders != null)
+ {
+ foreach (var head in dicHeaders)
+ {
+ requestContent.Headers.Add(head.Key, head.Value);
+ }
+ }
+ client.Timeout = TimeSpan.FromSeconds(timeoutSecond);
+ var response = await client.PatchAsync(url, requestContent);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+
+ public async Task DeleteAsync(string url, Dictionary dicHeaders, int timeoutSecond)
+ {
+ var client = _httpClientFactory.CreateClient();
+ var request = new HttpRequestMessage(HttpMethod.Delete, url);
+ if (dicHeaders != null)
+ {
+ foreach (var head in dicHeaders)
+ {
+ request.Headers.Add(head.Key, head.Value);
+ }
+ }
+ client.Timeout = TimeSpan.FromSeconds(timeoutSecond);
+ var response = await client.SendAsync(request);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+
+ ///
+ /// 异步请求(通用)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task ExecuteAsync(string url, HttpMethod method, string requestString, Dictionary dicHeaders, int timeoutSecond = 120,
+ string accept = "application/json")
+ {
+ var client = _httpClientFactory.CreateClient();
+ if (url.IndexOf('?') > -1)
+ {
+ url += "&v=" + DateTime.Now.ToString("yyyyMMddhhmmss");
+ }
+ else
+ {
+ url += "?v=" + DateTime.Now.ToString("yyyyMMddhhmmss");
+ }
+ var request = new HttpRequestMessage(method, url)
+ {
+ Content = new StringContent(requestString),
+ };
+ request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+ if (dicHeaders != null)
+ {
+ foreach (var header in dicHeaders)
+ {
+ request.Headers.Add(header.Key, header.Value);
+ }
+ }
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(accept) { CharSet = "utf-8" });
+ var response = await client.SendAsync(request);
+ if (response.IsSuccessStatusCode)
+ {
+ var result = await response.Content.ReadAsStringAsync();
+ return result;
+ }
+ else
+ {
+ throw new CustomerHttpException($"接口请求错误,错误代码{response.StatusCode},错误原因{response.ReasonPhrase}");
+ }
+ }
+ }
+
+ public class CustomerHttpException : Exception
+ {
+ public CustomerHttpException() : base()
+ { }
+
+ public CustomerHttpException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterCloud.Code/Util/JsonHelper.cs b/WaterCloud.Code/Util/JsonHelper.cs
new file mode 100644
index 0000000..d5e07f8
--- /dev/null
+++ b/WaterCloud.Code/Util/JsonHelper.cs
@@ -0,0 +1,138 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+
+namespace WaterCloud.Code
+{
+ #region JsonHelper
+
+ public static class JsonHelper
+ {
+ ///
+ /// 把数组转为逗号连接的字符串
+ ///
+ ///
+ ///
+ ///
+ public static string ArrayToString(dynamic data, string Str)
+ {
+ string resStr = Str;
+ foreach (var item in data)
+ {
+ if (resStr != "")
+ {
+ resStr += ",";
+ }
+
+ if (item is string)
+ {
+ resStr += item;
+ }
+ else
+ {
+ resStr += item.Value;
+ }
+ }
+ return resStr;
+ }
+
+ public static object ToObject(this string Json)
+ {
+ return string.IsNullOrEmpty(Json) ? null : JsonConvert.DeserializeObject(Json);
+ }
+
+ public static T ToObject(this string Json)
+ {
+ Json = Json.Replace(" ", "");
+ return Json == null ? default(T) : JsonConvert.DeserializeObject(Json);
+ }
+
+ public static JObject ToJObject(this string Json)
+ {
+ return Json == null ? JObject.Parse("{}") : JObject.Parse(Json.Replace(" ", ""));
+ }
+
+ public static List ToList(this string Json)
+ {
+ return Json == null ? null : JsonConvert.DeserializeObject