Initial_commit_SecMPS_v2
This commit is contained in:
87
.deepseek/instructions.md
Normal file
87
.deepseek/instructions.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Project Structure (Auto-generated)
|
||||
|
||||
> This file was automatically generated by DeepSeek TUI.
|
||||
> You can edit or delete it at any time.
|
||||
|
||||
**Summary:** A JavaScript/Node.js project
|
||||
|
||||
**Tree:**
|
||||
```
|
||||
DIR: api_sqlsugar
|
||||
FILE: .dockerignore
|
||||
FILE: .editorconfig
|
||||
FILE: README.md
|
||||
DIR: VolPro.Builder
|
||||
DIR: VolPro.Core
|
||||
DIR: VolPro.DbTest
|
||||
DIR: VolPro.Entity
|
||||
DIR: VolPro.Mes
|
||||
DIR: VolPro.Sys
|
||||
DIR: VolPro.WH
|
||||
DIR: VolPro.WebApi
|
||||
FILE: VolPro.sln
|
||||
FILE: VolPro.v12.suo
|
||||
FILE: VolPro.vssscc
|
||||
DIR: Warehouse
|
||||
FILE: sqlsugar适配差异说明.txt
|
||||
DIR: doc
|
||||
DIR: 对接文档
|
||||
DIR: 整合方案
|
||||
FILE: owl_api_research.md
|
||||
DIR: owl_zlmediakit
|
||||
FILE: README.md
|
||||
DIR: configs
|
||||
DIR: deb-packages
|
||||
DIR: docker-images
|
||||
DIR: scripts
|
||||
DIR: source-packages
|
||||
FILE: 排查手册-访问超时.md
|
||||
FILE: 联网部署手册.md
|
||||
FILE: 部署手册.md
|
||||
DIR: warehouse
|
||||
DIR: .trae
|
||||
DIR: .vscode
|
||||
FILE: README.md
|
||||
DIR: dist
|
||||
DIR: doc
|
||||
DIR: docs
|
||||
FILE: env.d.ts
|
||||
FILE: index.html
|
||||
DIR: node_modules
|
||||
FILE: package-lock.json
|
||||
FILE: package.json
|
||||
DIR: public
|
||||
DIR: src
|
||||
FILE: tsconfig.app.json
|
||||
FILE: tsconfig.json
|
||||
FILE: tsconfig.node.json
|
||||
FILE: vite.config.ts
|
||||
DIR: web.vite
|
||||
DIR: .VSCodeCounter
|
||||
FILE: .browserslistrc
|
||||
FILE: .eslintrc.cjs
|
||||
FILE: .eslintrc.js
|
||||
DIR: .idea
|
||||
FILE: .prettierrc.json
|
||||
DIR: .vscode
|
||||
FILE: README.md
|
||||
FILE: babel.config.js
|
||||
FILE: build.bat
|
||||
DIR: dist
|
||||
FILE: env.d.ts
|
||||
FILE: index.html
|
||||
FILE: install.bat
|
||||
DIR: node_modules
|
||||
FILE: package-lock.json
|
||||
FILE: package.json
|
||||
DIR: public
|
||||
FILE: report_form_rollup-plugin-visualizer.html
|
||||
FILE: run.bat
|
||||
DIR: src
|
||||
FILE: temp.json
|
||||
FILE: tsconfig.app.json
|
||||
FILE: tsconfig.json
|
||||
FILE: tsconfig.node.json
|
||||
FILE: vite.config.ts
|
||||
FILE: volar.config.js
|
||||
```
|
||||
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
node_modules/
|
||||
dist/
|
||||
bin/
|
||||
obj/
|
||||
*.user
|
||||
*.suo
|
||||
*.vs/
|
||||
.idea/
|
||||
*.swp
|
||||
.vscode/
|
||||
*.log
|
||||
temp.json
|
||||
*.bak
|
||||
tmp.bat
|
||||
report_form_rollup-plugin-visualizer.html
|
||||
25
api_sqlsugar/.dockerignore
Normal file
25
api_sqlsugar/.dockerignore
Normal file
@@ -0,0 +1,25 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
4
api_sqlsugar/.editorconfig
Normal file
4
api_sqlsugar/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.cs]
|
||||
|
||||
# CS8604: 引用类型参数可能为 null。
|
||||
dotnet_diagnostic.CS8604.severity = silent
|
||||
1
api_sqlsugar/README.md
Normal file
1
api_sqlsugar/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# api_sqlsugar
|
||||
8
api_sqlsugar/VolPro.Builder/Class1.cs
Normal file
8
api_sqlsugar/VolPro.Builder/Class1.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace VolPro.Builder
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
namespace VolPro.Builder.IRepositories
|
||||
{
|
||||
public partial interface ISys_TableInfoRepository : IDependency,IRepository<Sys_TableInfo>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Builder.IServices
|
||||
{
|
||||
public partial interface ISys_TableInfoService : IService<Sys_TableInfo>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Builder.IServices
|
||||
{
|
||||
public partial interface ISys_TableInfoService
|
||||
{
|
||||
Task<(string, string)> GetTableTree();
|
||||
|
||||
string CreateEntityModel(Sys_TableInfo tableInfo);
|
||||
|
||||
WebResponseContent SaveEidt(Sys_TableInfo sysTableInfo);
|
||||
|
||||
string CreateServices(string tableName, string nameSpace, string foldername, bool webController, bool apiController);
|
||||
|
||||
|
||||
string CreateVuePage(Sys_TableInfo sysTableInfo, string vuePath, int tableId, string table);
|
||||
|
||||
object LoadTable(Sys_TableInfo sysTableInfo, int parentId, string tableName, string columnCNName, string nameSpace, string foldername, int table_Id, bool isTreeLoad, string dbServer);
|
||||
Task<WebResponseContent> SyncTable(string tableName);
|
||||
Task<WebResponseContent> DelTree(int table_Id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.Services;
|
||||
|
||||
namespace VolPro.Builder.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 按数据库类型实现的表结构操作策略接口。
|
||||
/// </summary>
|
||||
public interface ITableDatabaseProvider
|
||||
{
|
||||
Task<bool> TableExistsAsync(string tableName);
|
||||
|
||||
Task CreateTableAsync(CreateTableRequest request);
|
||||
|
||||
Task<List<string>> GetAllTablesAsync();
|
||||
|
||||
Task<TableInfoDto> GetTableInfoAsync(string tableName);
|
||||
|
||||
Task UpdateTableAsync(UpdateTableRequest request);
|
||||
|
||||
Task DeleteTableAsync(string tableName);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Builder.IServices
|
||||
{
|
||||
public interface ITableProviderFactory
|
||||
{
|
||||
ITableDatabaseProvider GetProvider(string dbService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Builder.IServices;
|
||||
|
||||
public interface ITableService
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断指定表是否存在。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识,例如 SqlServer / MySql / PGSql,如果为 null 则使用默认配置。</param>
|
||||
Task<bool> TableExistsAsync(string dbService, string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// 创建表。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识。</param>
|
||||
Task<WebResponseContent> CreateTableAsync(string dbService, CreateTableRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有表名。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识。</param>
|
||||
Task<object> GetAllTablesAsync(string dbService);
|
||||
|
||||
/// <summary>
|
||||
/// 获取表结构信息。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识。</param>
|
||||
Task<TableInfoDto> GetTableInfoAsync(string dbService, string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// 更新表结构。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识。</param>
|
||||
Task<WebResponseContent> UpdateTableAsync(string dbService, UpdateTableRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// 删除表。
|
||||
/// </summary>
|
||||
/// <param name="dbService">数据库类型标识。</param>
|
||||
Task<WebResponseContent> DeleteTableAsync(string dbService, string tableName);
|
||||
}
|
||||
27
api_sqlsugar/VolPro.Builder/Properties/launchSettings.json
Normal file
27
api_sqlsugar/VolPro.Builder/Properties/launchSettings.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:34188/",
|
||||
"sslPort": 44309
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"VolPro.Builder": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using VolPro.Builder.IRepositories;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Builder.Repositories
|
||||
{
|
||||
public partial class Sys_TableInfoRepository : RepositoryBase<Sys_TableInfo>, ISys_TableInfoRepository
|
||||
{
|
||||
public Sys_TableInfoRepository(SysDbContext dbContext)
|
||||
: base(dbContext)
|
||||
{
|
||||
|
||||
}
|
||||
public static ISys_TableInfoRepository GetService
|
||||
{
|
||||
get { return AutofacContainerModule.GetService<ISys_TableInfoRepository>(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Text.RegularExpressions;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Builder.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// SQL 表结构信息
|
||||
/// </summary>
|
||||
public class SqlColumnSchemaInfo
|
||||
{
|
||||
/// <summary>字段名</summary>
|
||||
public string ColumnName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>备注(说明)</summary>
|
||||
/// <remarks>对任意 SQL 结果集,通常无法获取扩展属性/注释,此处可能为空</remarks>
|
||||
public string Comment { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>长度/精度</summary>
|
||||
public int? Length { get; set; }
|
||||
|
||||
/// <summary>C# 类型(如 String、Int32、Decimal 等)</summary>
|
||||
public string DataType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>数据库字段类型(如 nvarchar、int、decimal 等)</summary>
|
||||
public string DbDataType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>小数位数(decimal/numeric 等)</summary>
|
||||
public int? Scale { get; set; }
|
||||
|
||||
/// <summary>是否可为空</summary>
|
||||
public bool IsNullable { get; set; } = true;
|
||||
|
||||
/// <summary>列序号</summary>
|
||||
public int Ordinal { get; set; }
|
||||
}
|
||||
public static class SqlSchemaHelper
|
||||
{
|
||||
public static (bool IsValid, string ErrorMessage) IsValidSql(string sql, string dbServer = "SysDbContext")
|
||||
{
|
||||
var dbType = DbRelativeCache.GetDbType(dbServer);
|
||||
var sanitized = sql;
|
||||
|
||||
if (ContainsForbiddenKeywords(sanitized, dbType, out var keyword))
|
||||
{
|
||||
return (false, $"sql包含禁止操作:{keyword}");
|
||||
}
|
||||
|
||||
if (ContainsDbSpecificForbiddenPatterns(sanitized, dbType, out var pattern))
|
||||
{
|
||||
return (false, $"sql包含禁止模式:{pattern}");
|
||||
}
|
||||
if (ContainsSysUserUserPwdReference(sanitized))
|
||||
{
|
||||
return (false, "禁止查询 `sys_user`.`UserPwd` 字段。");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(sql))
|
||||
return (true, string.Empty);
|
||||
var connection = GetDbConnection(dbServer);
|
||||
if (connection.State == ConnectionState.Closed)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
try
|
||||
{
|
||||
using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
using var reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
|
||||
return (true, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"sql执行未通过,请检查sql,{ex.InnerException?.Message ?? ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open)
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContainsForbiddenKeywords(string statement, string dbType, out string keyword)
|
||||
{
|
||||
keyword = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(statement)) return false;
|
||||
// 全字匹配:SQL 任意位置出现关键字即拦截
|
||||
var forbiddenWords = new (string Word, string Label)[]
|
||||
{
|
||||
("delete", "DELETE"),
|
||||
("drop", "DROP"),
|
||||
("truncate", "TRUNCATE"),
|
||||
("alter", "ALTER"),
|
||||
("create", "CREATE"),
|
||||
("grant", "GRANT"),
|
||||
("revoke", "REVOKE"),
|
||||
("call", "CALL"),
|
||||
("exec", "EXEC"),
|
||||
("execute", "EXECUTE"),
|
||||
("commit", "COMMIT"),
|
||||
("rollback", "ROLLBACK"),
|
||||
("use", "USE"),
|
||||
("into", "INTO")
|
||||
};
|
||||
|
||||
foreach (var (word, label) in forbiddenWords)
|
||||
{
|
||||
if (Regex.IsMatch(statement, $@"\b{Regex.Escape(word)}\b", RegexOptions.IgnoreCase))
|
||||
{
|
||||
keyword = label;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// MySQL:SELECT ... INTO OUTFILE|DUMPFILE(导出文件/写出)
|
||||
if (string.Equals(dbType, "MySql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Regex.IsMatch(statement, @"\binto\s+(?:out|dump)file\b", RegexOptions.IgnoreCase))
|
||||
{
|
||||
keyword = "INTO OUTFILE/DUMPFILE";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ContainsSysUserUserPwdReference(string statement)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(statement)) return false;
|
||||
bool hasSysUser = Regex.IsMatch(statement, @"\bsys_user\b", RegexOptions.IgnoreCase);
|
||||
if (!hasSysUser) return false;
|
||||
|
||||
bool hasUserPwd =
|
||||
Regex.IsMatch(statement, @"\bUserPwd\b", RegexOptions.IgnoreCase)
|
||||
|| Regex.IsMatch(statement, @"\[`UserPwd`\]", RegexOptions.IgnoreCase)
|
||||
|| Regex.IsMatch(statement, @"\[\s*UserPwd\s*\]", RegexOptions.IgnoreCase)
|
||||
|| Regex.IsMatch(statement, @"""UserPwd""", RegexOptions.IgnoreCase);
|
||||
|
||||
return hasUserPwd;
|
||||
}
|
||||
|
||||
private static bool ContainsDbSpecificForbiddenPatterns(string statement, string dbType, out string pattern)
|
||||
{
|
||||
pattern = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(statement)) return false;
|
||||
|
||||
if (string.Equals(dbType, "MsSql", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(dbType, "PgSql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// SELECT ... INTO <table> ... (创建表语义)
|
||||
// SQL Server:允许临时表 INTO #temp;拒绝其它 into 创建表
|
||||
// PostgreSQL:允许 INTO TEMP|TEMPORARY;拒绝其它 into 创建表
|
||||
if (string.Equals(dbType, "MsSql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Regex.IsMatch(statement,
|
||||
@"\bselect\b[\s\S]*?\binto\b\s+(?!#)[^\s]+\s+\bfrom\b",
|
||||
RegexOptions.IgnoreCase))
|
||||
{
|
||||
pattern = "SELECT ... INTO table (non-temp)";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Regex.IsMatch(statement,
|
||||
@"\bselect\b[\s\S]*?\binto\b\s+(?!temp(?:orary)?\b)[^\s]+\s+\bfrom\b",
|
||||
RegexOptions.IgnoreCase))
|
||||
{
|
||||
pattern = "SELECT ... INTO table";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(dbType, "PgSql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// COPY FROM/COPY ... 会修改数据或导入,禁止
|
||||
if (Regex.IsMatch(statement, @"\bcopy\b", RegexOptions.IgnoreCase))
|
||||
{
|
||||
pattern = "COPY";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(dbType, "Oracle", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static IDbConnection GetDbConnection(string dbServer = "SysDbContext")
|
||||
{
|
||||
var type = DbRelativeCache.GetDbContextType(dbServer);
|
||||
object dbObj = HttpContext.Current.RequestServices.GetService(type);
|
||||
BaseDbContext context = (BaseDbContext)dbObj;
|
||||
var connection = context.SqlSugarClient.Ado.Connection;
|
||||
return connection;
|
||||
}
|
||||
public static List<SqlColumnSchemaInfo> GetSchemaFromSql(string sql, string dbServer = "SysDbContext")
|
||||
{
|
||||
List<SqlColumnSchemaInfo> list = new List<SqlColumnSchemaInfo>();
|
||||
var connection = GetDbConnection(dbServer);
|
||||
if (connection.State == ConnectionState.Closed)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
try
|
||||
{
|
||||
using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
using var reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
|
||||
if (reader == null) return list;
|
||||
|
||||
var schemaTable = reader.GetSchemaTable();
|
||||
if (schemaTable == null || schemaTable.Rows.Count == 0)
|
||||
return list;
|
||||
|
||||
int ordinal = 0;
|
||||
foreach (DataRow row in schemaTable.Rows)
|
||||
{
|
||||
var info = RowToColumnInfo(row, reader, ordinal++);
|
||||
if (info != null)
|
||||
list.Add(info);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.Message, ex.InnerException);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open)
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static SqlColumnSchemaInfo RowToColumnInfo(DataRow row, IDataReader reader, int ordinal)
|
||||
{
|
||||
var colName = GetRowValue<string>(row, "ColumnName")
|
||||
?? GetRowValue<string>(row, "BaseColumnName");
|
||||
|
||||
var dataType = GetRowValue<Type>(row, "DataType");
|
||||
var netTypeName = dataType?.Name ?? GetRowValue<string>(row, "DataTypeName") ?? string.Empty;
|
||||
|
||||
var dbDataType = string.Empty;
|
||||
if (reader != null && ordinal >= 0 && ordinal < reader.FieldCount)
|
||||
{
|
||||
try { dbDataType = reader.GetDataTypeName(ordinal) ?? string.Empty; }
|
||||
catch { }
|
||||
}
|
||||
if (string.IsNullOrEmpty(dbDataType))
|
||||
dbDataType = GetRowValue<string>(row, "DataTypeName") ?? string.Empty;
|
||||
|
||||
var length = GetRowValue<int?>(row, "ColumnSize");
|
||||
var precision = GetRowValue<int?>(row, "NumericPrecision");
|
||||
var scale = GetRowValue<int?>(row, "NumericScale");
|
||||
|
||||
if (length.HasValue && length.Value <= 0)
|
||||
length = null;
|
||||
if (dataType != null && (netTypeName.Contains("Decimal", StringComparison.OrdinalIgnoreCase) || netTypeName.Contains("Numeric", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
length = precision;
|
||||
}
|
||||
|
||||
var allowNull = true;
|
||||
if (row.Table.Columns.Contains("AllowDBNull"))
|
||||
{
|
||||
var allowNullVal = row["AllowDBNull"];
|
||||
if (allowNullVal != null && allowNullVal != DBNull.Value)
|
||||
allowNull = Convert.ToBoolean(allowNullVal);
|
||||
}
|
||||
|
||||
var csharpTypeName = DbTypeToCSharpType(dbDataType, dataType, netTypeName);
|
||||
|
||||
return new SqlColumnSchemaInfo
|
||||
{
|
||||
ColumnName = colName ?? string.Empty,
|
||||
Comment = string.Empty,
|
||||
Length = length,
|
||||
Scale = scale,
|
||||
DataType = csharpTypeName,
|
||||
DbDataType = dbDataType,
|
||||
IsNullable = allowNull,
|
||||
Ordinal = ordinal
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据库字段类型转换为 C# 类型名称(如 int、string、DateTime、decimal 等)
|
||||
/// 参照 Sys_TableInfoService 的 GetSqlServerStructure/GetMySqlStructure/GetPgSqlStructure 映射
|
||||
/// </summary>
|
||||
private static string DbTypeToCSharpType(string dbDataType, Type netType, string netTypeName)
|
||||
{
|
||||
var dbLower = (dbDataType ?? string.Empty).Trim().ToLowerInvariant();
|
||||
if (!string.IsNullOrEmpty(dbLower))
|
||||
{
|
||||
// SQL Server、MySQL、PostgreSQL 等统一映射
|
||||
switch (dbLower)
|
||||
{
|
||||
case "uniqueidentifier":
|
||||
case "uuid":
|
||||
return "guid";
|
||||
case "bit":
|
||||
case "bool":
|
||||
case "boolean":
|
||||
return "bool";
|
||||
case "tinyint":
|
||||
return "byte";
|
||||
case "smallint":
|
||||
case "int2":
|
||||
return "short";
|
||||
case "int":
|
||||
case "integer":
|
||||
case "mediumint":
|
||||
case "int4":
|
||||
case "year":
|
||||
return "int";
|
||||
case "bigint":
|
||||
case "int8":
|
||||
return "long";
|
||||
case "real":
|
||||
case "float4":
|
||||
return "float";
|
||||
case "float":
|
||||
case "double":
|
||||
case "float8":
|
||||
return "double";
|
||||
case "decimal":
|
||||
case "numeric":
|
||||
case "money":
|
||||
case "smallmoney":
|
||||
return "decimal";
|
||||
case "time":
|
||||
return "TimeSpan";
|
||||
case "date":
|
||||
case "datetime":
|
||||
case "datetime2":
|
||||
case "smalldatetime":
|
||||
case "timestamp":
|
||||
return "DateTime";
|
||||
case "char":
|
||||
case "varchar":
|
||||
case "nvarchar":
|
||||
case "nchar":
|
||||
case "text":
|
||||
case "ntext":
|
||||
case "xml":
|
||||
case "varbinary":
|
||||
case "image":
|
||||
case "tinytext":
|
||||
case "mediumtext":
|
||||
case "longtext":
|
||||
case "tinyblob":
|
||||
case "blob":
|
||||
case "mediumblob":
|
||||
case "longblob":
|
||||
case "bytea":
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
|
||||
// 无 DbDataType 或未匹配时,按 .NET Type 映射
|
||||
if (netType != null)
|
||||
{
|
||||
if (netType == typeof(bool) || netType == typeof(bool?)) return "bool";
|
||||
if (netType == typeof(byte) || netType == typeof(byte?)) return "byte";
|
||||
if (netType == typeof(short) || netType == typeof(short?)) return "short";
|
||||
if (netType == typeof(int) || netType == typeof(int?)) return "int";
|
||||
if (netType == typeof(long) || netType == typeof(long?)) return "long";
|
||||
if (netType == typeof(float) || netType == typeof(float?)) return "float";
|
||||
if (netType == typeof(double) || netType == typeof(double?)) return "double";
|
||||
if (netType == typeof(decimal) || netType == typeof(decimal?)) return "decimal";
|
||||
if (netType == typeof(DateTime) || netType == typeof(DateTime?)) return "DateTime";
|
||||
if (netType == typeof(TimeSpan) || netType == typeof(TimeSpan?)) return "TimeSpan";
|
||||
if (netType == typeof(Guid) || netType == typeof(Guid?)) return "guid";
|
||||
if (netType == typeof(string)) return "string";
|
||||
if (netType == typeof(byte[])) return "byte[]";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(netTypeName))
|
||||
{
|
||||
if (netTypeName.IndexOf("Boolean", StringComparison.OrdinalIgnoreCase) >= 0) return "bool";
|
||||
if (netTypeName.IndexOf("Byte", StringComparison.OrdinalIgnoreCase) >= 0) return "byte";
|
||||
if (netTypeName.IndexOf("Int16", StringComparison.OrdinalIgnoreCase) >= 0) return "short";
|
||||
if (netTypeName.IndexOf("Int32", StringComparison.OrdinalIgnoreCase) >= 0) return "int";
|
||||
if (netTypeName.IndexOf("Int64", StringComparison.OrdinalIgnoreCase) >= 0) return "long";
|
||||
if (netTypeName.IndexOf("Single", StringComparison.OrdinalIgnoreCase) >= 0) return "float";
|
||||
if (netTypeName.IndexOf("Double", StringComparison.OrdinalIgnoreCase) >= 0) return "double";
|
||||
if (netTypeName.IndexOf("Decimal", StringComparison.OrdinalIgnoreCase) >= 0 || netTypeName.IndexOf("Numeric", StringComparison.OrdinalIgnoreCase) >= 0) return "decimal";
|
||||
if (netTypeName.IndexOf("DateTime", StringComparison.OrdinalIgnoreCase) >= 0) return "DateTime";
|
||||
if (netTypeName.IndexOf("TimeSpan", StringComparison.OrdinalIgnoreCase) >= 0) return "TimeSpan";
|
||||
if (netTypeName.IndexOf("Guid", StringComparison.OrdinalIgnoreCase) >= 0) return "guid";
|
||||
if (netTypeName.IndexOf("String", StringComparison.OrdinalIgnoreCase) >= 0) return "string";
|
||||
}
|
||||
|
||||
return "string";
|
||||
}
|
||||
|
||||
private static T GetRowValue<T>(DataRow row, string columnName)
|
||||
{
|
||||
if (!row.Table.Columns.Contains(columnName))
|
||||
return default;
|
||||
|
||||
var val = row[columnName];
|
||||
if (val == null || val == DBNull.Value)
|
||||
return default;
|
||||
|
||||
var targetType = typeof(T);
|
||||
if (targetType == typeof(string))
|
||||
return (T)(object)(val?.ToString() ?? string.Empty);
|
||||
if (targetType == typeof(Type))
|
||||
return val is Type t ? (T)(object)t : default;
|
||||
if (targetType == typeof(int?) || targetType == typeof(int))
|
||||
{
|
||||
try { return (T)(object)Convert.ToInt32(val); }
|
||||
catch { return default; }
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType;
|
||||
return (T)Convert.ChangeType(val, underlyingType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
using VolPro.Builder.IRepositories;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Builder.Services
|
||||
{
|
||||
public partial class Sys_TableInfoService : ServiceBase<Sys_TableInfo, ISys_TableInfoRepository>, ISys_TableInfoService, IDependency
|
||||
{
|
||||
public Sys_TableInfoService(ISys_TableInfoRepository repository)
|
||||
: base(repository)
|
||||
{
|
||||
Init(repository);
|
||||
}
|
||||
public static ISys_TableInfoService Instance
|
||||
{
|
||||
get { return AutofacContainerModule.GetService<ISys_TableInfoService>(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class CreateTableRequest
|
||||
{
|
||||
[Required]
|
||||
public string TableName { get; set; } = string.Empty;
|
||||
|
||||
public List<TableColumnDto> Columns { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class TableColumnDto
|
||||
{
|
||||
public string ColumnName { get; set; } = string.Empty;
|
||||
public string DataType { get; set; } = "nvarchar";
|
||||
public int? Length { get; set; }
|
||||
// decimal/numeric 的小数位(scale)
|
||||
public int? Scale { get; set; }
|
||||
public bool IsNullable { get; set; } = true;
|
||||
public bool IsPrimaryKey { get; set; } = false;
|
||||
public bool IsIdentity { get; set; } = false;
|
||||
public string Comment { get; set; }
|
||||
public int Order { get; set; }
|
||||
|
||||
public string DefaultValue { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 数据库字段(true=来自数据库,false=新增)
|
||||
/// </summary>
|
||||
public bool IsDbField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始字段名(用于区分重命名与删除:重命名时 OriginalColumnName 为旧名)
|
||||
/// </summary>
|
||||
public string OriginalColumnName { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class TableInfoDto
|
||||
{
|
||||
public string TableName { get; set; } = string.Empty;
|
||||
public List<TableColumnDto> Columns { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class UpdateTableRequest
|
||||
{
|
||||
[Required]
|
||||
public string TableName { get; set; } = string.Empty;
|
||||
|
||||
public List<TableColumnDto> Columns { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.EFDbContext;
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
/// <summary>
|
||||
/// MySQL 数据库表结构操作实现。
|
||||
/// </summary>
|
||||
public class MySqlTableProvider : ITableDatabaseProvider
|
||||
{
|
||||
private readonly BaseDbContext _context;
|
||||
|
||||
public MySqlTableProvider(BaseDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> TableExistsAsync(string tableName)
|
||||
{
|
||||
const string mysqlSql = @"SELECT COUNT(*) as Value
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @tableName;";
|
||||
var res = await _context.SqlSugarClient.Ado.GetScalarAsync(mysqlSql, new { tableName });
|
||||
return Convert.ToInt32(res) > 0;
|
||||
}
|
||||
|
||||
public async Task CreateTableAsync(CreateTableRequest request)
|
||||
{
|
||||
await CreateTableMySqlAsync(request);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAllTablesAsync()
|
||||
{
|
||||
const string mysqlSql = @"
|
||||
SELECT TABLE_NAME as Value
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
ORDER BY TABLE_NAME";
|
||||
var rows = await _context.SqlSugarClient.Ado.SqlQueryAsync<string>(mysqlSql);
|
||||
return rows ?? new List<string>();
|
||||
}
|
||||
|
||||
public async Task<TableInfoDto> GetTableInfoAsync(string tableName)
|
||||
{
|
||||
return await GetTableInfoMySqlAsync(tableName);
|
||||
}
|
||||
|
||||
public async Task UpdateTableAsync(UpdateTableRequest request)
|
||||
{
|
||||
await UpdateTableMySqlAsync(request);
|
||||
}
|
||||
|
||||
public async Task DeleteTableAsync(string tableName)
|
||||
{
|
||||
var dropMySql = $"DROP TABLE `{tableName.Replace("`", "``")}`;";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropMySql);
|
||||
}
|
||||
|
||||
private static string NormalizeMySqlDefault(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||
var s = raw.Trim();
|
||||
// MySQL BIT 类型默认值格式为 b'1' 或 b'0',统一返回 1 或 0
|
||||
if (s.StartsWith("b'", StringComparison.OrdinalIgnoreCase) && s.EndsWith("'") && s.Length >= 4)
|
||||
{
|
||||
var inner = s[2..^1];
|
||||
return inner == "1" ? "1" : inner == "0" ? "0" : inner;
|
||||
}
|
||||
if (s.Length >= 2 && s.StartsWith("'") && s.EndsWith("'"))
|
||||
return s[1..^1].Replace("''", "'");
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string FormatDefaultForMySql(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
var v = value.Trim();
|
||||
var vLower = v.ToLower();
|
||||
if (vLower is "current_timestamp" or "now()" or "now(6)")
|
||||
return $" DEFAULT {v}";
|
||||
if (long.TryParse(v, out _) || decimal.TryParse(v, out _))
|
||||
return $" DEFAULT {v}";
|
||||
if (vLower == "true" || vLower == "1") return " DEFAULT 1";
|
||||
if (vLower == "false" || vLower == "0") return " DEFAULT 0";
|
||||
return $" DEFAULT '{v.Replace("'", "''")}'";
|
||||
}
|
||||
|
||||
// MySQL 类型映射:根据通用 DataType/Length/Scale 生成 MySQL 列类型
|
||||
private static string BuildMySqlColumnType(TableColumnDto column)
|
||||
{
|
||||
var dt = (column.DataType ?? string.Empty).Trim();
|
||||
var dtLower = dt.ToLower();
|
||||
|
||||
return dtLower switch
|
||||
{
|
||||
"nvarchar" or "varchar" or "character varying" => $"VARCHAR({(column.Length is > 0 and <= 65535 ? column.Length : 255)})",
|
||||
"char" or "nchar" => $"CHAR({(column.Length is > 0 and <= 255 ? column.Length : 1)})",
|
||||
"nvarchar(max)" or "varchar(max)" => "LONGTEXT",
|
||||
"int" or "integer" => "INT",
|
||||
"bigint" => "BIGINT",
|
||||
"tinyint" => "TINYINT",
|
||||
"bit" => "BIT",
|
||||
"decimal" or "numeric" =>
|
||||
$"DECIMAL({(column.Length ?? 18)},{(column.Scale ?? 0)})",
|
||||
"double" or "float" => "DOUBLE",
|
||||
"datetime" => "DATETIME",
|
||||
"date" => "DATE",
|
||||
"text" or "ntext" => "TEXT",
|
||||
"longtext" => "LONGTEXT",
|
||||
"uniqueidentifier" or "uuid" => "CHAR(36)",
|
||||
_ => dt // 其他:假设是合法的 MySQL 类型
|
||||
};
|
||||
}
|
||||
|
||||
// MySQL 专用:建表
|
||||
private async Task CreateTableMySqlAsync(CreateTableRequest request)
|
||||
{
|
||||
var columnDefs = new List<string>();
|
||||
foreach (var column in request.Columns.OrderBy(c => c.Order))
|
||||
{
|
||||
var typeSql = BuildMySqlColumnType(column);
|
||||
var isIdentity = column.IsIdentity;
|
||||
var nullSql = isIdentity ? "NOT NULL" : (column.IsNullable ? "NULL" : "NOT NULL");
|
||||
var identitySql = isIdentity ? " AUTO_INCREMENT" : string.Empty;
|
||||
var commentSql = string.IsNullOrWhiteSpace(column.Comment)
|
||||
? string.Empty
|
||||
: $" COMMENT '{column.Comment.Replace("'", "''")}'";
|
||||
var defaultSql = FormatDefaultForMySql(column.DefaultValue);
|
||||
|
||||
columnDefs.Add($"`{column.ColumnName}` {typeSql}{identitySql} {nullSql}{defaultSql}{commentSql}");
|
||||
}
|
||||
|
||||
var pkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => $"`{c.ColumnName}`");
|
||||
var pkSql = pkCols.Any() ? $", PRIMARY KEY ({string.Join(", ", pkCols)})" : string.Empty;
|
||||
var createSql = $"CREATE TABLE `{request.TableName}` (\n {string.Join(",\n ", columnDefs)}{pkSql}\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createSql);
|
||||
}
|
||||
|
||||
// MySQL 专用:获取表结构
|
||||
private async Task<TableInfoDto> GetTableInfoMySqlAsync(string tableName)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT
|
||||
c.COLUMN_NAME,
|
||||
c.DATA_TYPE,
|
||||
c.CHARACTER_MAXIMUM_LENGTH,
|
||||
c.IS_NULLABLE,
|
||||
c.ORDINAL_POSITION,
|
||||
c.NUMERIC_SCALE,
|
||||
c.NUMERIC_PRECISION,
|
||||
CASE WHEN k.CONSTRAINT_NAME = 'PRIMARY' THEN 1 ELSE 0 END AS IS_PRIMARY_KEY,
|
||||
CASE WHEN c.EXTRA LIKE '%auto_increment%' THEN 1 ELSE 0 END AS IS_IDENTITY,
|
||||
c.COLUMN_COMMENT,
|
||||
c.COLUMN_DEFAULT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
|
||||
ON k.TABLE_SCHEMA = c.TABLE_SCHEMA
|
||||
AND k.TABLE_NAME = c.TABLE_NAME
|
||||
AND k.COLUMN_NAME = c.COLUMN_NAME
|
||||
WHERE c.TABLE_SCHEMA = DATABASE() AND c.TABLE_NAME = @tableName
|
||||
ORDER BY c.ORDINAL_POSITION;";
|
||||
|
||||
var dt = await _context.SqlSugarClient.Ado.GetDataTableAsync(sql, new { tableName });
|
||||
var columns = new List<TableColumnDto>();
|
||||
foreach (DataRow row in dt.Rows)
|
||||
{
|
||||
var dataType = row[1]?.ToString() ?? "";
|
||||
var isDecimal = dataType.Equals("decimal", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
int? charLen = null;
|
||||
if (!dataType.Equals("text", StringComparison.OrdinalIgnoreCase) &&
|
||||
!dataType.Equals("longtext", StringComparison.OrdinalIgnoreCase) &&
|
||||
row[2] != DBNull.Value && row[2] != null)
|
||||
{
|
||||
var raw = Convert.ToInt64(row[2]);
|
||||
if (raw > 0 && raw <= int.MaxValue) charLen = (int)raw;
|
||||
}
|
||||
|
||||
var numericScale = row[5] == DBNull.Value || row[5] == null ? (int?)null : Convert.ToInt32(row[5]);
|
||||
var numericPrecision = row[6] == DBNull.Value || row[6] == null ? (int?)null : Convert.ToInt32(row[6]);
|
||||
var colDefault = row[10] == DBNull.Value || row[10] == null ? null : NormalizeMySqlDefault(row[10]?.ToString());
|
||||
|
||||
columns.Add(new TableColumnDto
|
||||
{
|
||||
ColumnName = row[0]?.ToString() ?? "",
|
||||
DataType = dataType,
|
||||
Length = isDecimal ? numericPrecision : charLen,
|
||||
Scale = isDecimal ? numericScale : null,
|
||||
IsNullable = string.Equals(row[3]?.ToString(), "YES", StringComparison.OrdinalIgnoreCase),
|
||||
Order = Convert.ToInt32(row[4]),
|
||||
IsPrimaryKey = row[7] != DBNull.Value && row[7] != null && Convert.ToInt32(row[7]) == 1,
|
||||
IsIdentity = row[8] != DBNull.Value && row[8] != null && Convert.ToInt32(row[8]) == 1,
|
||||
Comment = row[9]?.ToString(),
|
||||
DefaultValue = colDefault ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return new TableInfoDto { TableName = tableName, Columns = columns };
|
||||
}
|
||||
|
||||
// MySQL 专用:更新表(仅支持新增列、修改类型/长度/小数位/可空、更新主键)
|
||||
private async Task UpdateTableMySqlAsync(UpdateTableRequest request)
|
||||
{
|
||||
var existing = await GetTableInfoMySqlAsync(request.TableName) ?? throw new InvalidOperationException("Table not found.");
|
||||
var existingByName = existing.Columns.ToDictionary(c => c.ColumnName, c => c, StringComparer.Ordinal);
|
||||
var newNames = request.Columns.Select(c => c.ColumnName).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// 1) 识别并执行重命名字段(区分大小写)
|
||||
var renames = request.Columns
|
||||
.Where(c => c.IsDbField && !string.IsNullOrWhiteSpace(c.OriginalColumnName) &&
|
||||
c.OriginalColumnName != c.ColumnName &&
|
||||
existingByName.Keys.Any(k => string.Equals(k, c.OriginalColumnName, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var r in renames)
|
||||
{
|
||||
var actualKey = existingByName.Keys.FirstOrDefault(k => string.Equals(k, r.OriginalColumnName, StringComparison.OrdinalIgnoreCase));
|
||||
if (actualKey == null) continue;
|
||||
var oldCol = existingByName[actualKey];
|
||||
var typeSql = BuildMySqlColumnType(oldCol);
|
||||
var isIdentity = oldCol.IsIdentity;
|
||||
var nullSql = isIdentity ? "NOT NULL" : (oldCol.IsNullable ? "NULL" : "NOT NULL");
|
||||
var identitySql = isIdentity ? " AUTO_INCREMENT" : string.Empty;
|
||||
var commentSql = string.IsNullOrWhiteSpace(oldCol.Comment) ? string.Empty : $" COMMENT '{oldCol.Comment.Replace("'", "''")}'";
|
||||
var defaultSql = FormatDefaultForMySql(oldCol.DefaultValue);
|
||||
var renameSql = $"ALTER TABLE `{request.TableName}` CHANGE COLUMN `{actualKey.Replace("`", "``")}` `{r.ColumnName.Replace("`", "``")}` {typeSql}{identitySql} {nullSql}{defaultSql}{commentSql};";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(renameSql);
|
||||
existingByName.Remove(actualKey);
|
||||
existingByName[r.ColumnName] = new TableColumnDto { ColumnName = r.ColumnName, DataType = oldCol.DataType, Length = oldCol.Length, Scale = oldCol.Scale, IsNullable = oldCol.IsNullable, IsPrimaryKey = oldCol.IsPrimaryKey, IsIdentity = oldCol.IsIdentity, Comment = oldCol.Comment, DefaultValue = oldCol.DefaultValue ?? "", Order = oldCol.Order };
|
||||
}
|
||||
|
||||
var existingNames = existingByName.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
var columnsToDrop = existingNames.Except(newNames, StringComparer.Ordinal).ToList();
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !existingByName.ContainsKey(c.ColumnName)).OrderBy(c => c.Order))
|
||||
{
|
||||
var typeSql = BuildMySqlColumnType(column);
|
||||
var isIdentity = column.IsIdentity;
|
||||
var nullSql = isIdentity ? "NOT NULL" : (column.IsNullable ? "NULL" : "NOT NULL");
|
||||
var identitySql = isIdentity ? " AUTO_INCREMENT" : string.Empty;
|
||||
var commentSql = string.IsNullOrWhiteSpace(column.Comment) ? string.Empty : $" COMMENT '{column.Comment.Replace("'", "''")}'";
|
||||
var defaultSql = FormatDefaultForMySql(column.DefaultValue);
|
||||
var addColSql = $"ALTER TABLE `{request.TableName}` ADD COLUMN `{column.ColumnName}` {typeSql}{identitySql} {nullSql}{defaultSql}{commentSql};";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addColSql);
|
||||
}
|
||||
|
||||
foreach (var column in request.Columns.Where(c => existingByName.ContainsKey(c.ColumnName)))
|
||||
{
|
||||
var old = existingByName[column.ColumnName];
|
||||
var typeSql = BuildMySqlColumnType(column);
|
||||
var oldTypeSql = BuildMySqlColumnType(old);
|
||||
var typeChanged = !string.Equals(oldTypeSql, typeSql, StringComparison.OrdinalIgnoreCase);
|
||||
var nullableChanged = old.IsNullable != column.IsNullable;
|
||||
var defaultChanged = (old.DefaultValue ?? "").Trim() != (column.DefaultValue ?? "").Trim();
|
||||
var identityChanged = old.IsIdentity != column.IsIdentity;
|
||||
var commentChanged = (old.Comment ?? "").Trim() != (column.Comment ?? "").Trim();
|
||||
|
||||
if (!typeChanged && !nullableChanged && !defaultChanged && !identityChanged && !commentChanged)
|
||||
continue;
|
||||
|
||||
var isIdentity = column.IsIdentity;
|
||||
var nullSql = isIdentity ? "NOT NULL" : (column.IsNullable ? "NULL" : "NOT NULL");
|
||||
var identitySql = isIdentity ? " AUTO_INCREMENT" : string.Empty;
|
||||
var commentSql = string.IsNullOrWhiteSpace(column.Comment) ? string.Empty : $" COMMENT '{column.Comment.Replace("'", "''")}'";
|
||||
var defaultSql = FormatDefaultForMySql(column.DefaultValue);
|
||||
var modifySql = $"ALTER TABLE `{request.TableName}` MODIFY COLUMN `{column.ColumnName}` {typeSql}{identitySql} {nullSql}{defaultSql}{commentSql};";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(modifySql);
|
||||
}
|
||||
|
||||
foreach (var colName in columnsToDrop)
|
||||
{
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE `{request.TableName}` DROP COLUMN `{colName}`;");
|
||||
}
|
||||
|
||||
var newPkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var existingPkCols = existingByName.Values.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var pkChanged = !newPkCols.SequenceEqual(existingPkCols);
|
||||
|
||||
if (pkChanged)
|
||||
{
|
||||
try { await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE `{request.TableName}` DROP PRIMARY KEY;"); } catch { }
|
||||
if (newPkCols.Any())
|
||||
{
|
||||
var pkColsSql = string.Join(", ", newPkCols.Select(c => $"`{c}`"));
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE `{request.TableName}` ADD PRIMARY KEY ({pkColsSql});");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Npgsql;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.EFDbContext;
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL 数据库表结构操作实现。
|
||||
/// </summary>
|
||||
public class PgSqlTableProvider : ITableDatabaseProvider
|
||||
{
|
||||
private readonly BaseDbContext _context;
|
||||
private readonly string _schema;
|
||||
|
||||
public PgSqlTableProvider(BaseDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
// 从 PG 连接字符串推断 schema:优先 SearchPath / search_path,其次 schema,默认 public
|
||||
var connStr = _context.SqlSugarClient.Ado.Connection?.ConnectionString;
|
||||
var schema = "public";
|
||||
if (!string.IsNullOrWhiteSpace(connStr))
|
||||
{
|
||||
try
|
||||
{
|
||||
var builder = new NpgsqlConnectionStringBuilder(connStr);
|
||||
if (!string.IsNullOrWhiteSpace(builder.SearchPath))
|
||||
{
|
||||
schema = builder.SearchPath.Split(',')[0].Trim();
|
||||
}
|
||||
else if (builder.TryGetValue("schema", out var sVal) && sVal is string s && !string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
schema = s.Trim();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 解析失败时回退到 public
|
||||
schema = "public";
|
||||
}
|
||||
}
|
||||
_schema = schema;
|
||||
}
|
||||
|
||||
public async Task<bool> TableExistsAsync(string tableName)
|
||||
{
|
||||
const string pgSql = "SELECT COUNT(*) as value FROM information_schema.tables WHERE table_schema = @schemaName AND table_name = @tableName;";
|
||||
var res = await _context.SqlSugarClient.Ado.GetScalarAsync(pgSql, new { schemaName = _schema, tableName });
|
||||
return Convert.ToInt32(res) > 0;
|
||||
}
|
||||
|
||||
public async Task CreateTableAsync(CreateTableRequest request)
|
||||
{
|
||||
await CreateTablePgSqlAsync(request);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAllTablesAsync()
|
||||
{
|
||||
const string pgSql = "SELECT table_name as value FROM information_schema.tables WHERE table_schema = @schemaName ORDER BY table_name;";
|
||||
var rows = await _context.SqlSugarClient.Ado.SqlQueryAsync<string>(pgSql, new { schemaName = _schema });
|
||||
return rows ?? new List<string>();
|
||||
}
|
||||
|
||||
public async Task<TableInfoDto> GetTableInfoAsync(string tableName)
|
||||
{
|
||||
return await GetTableInfoPgSqlAsync(tableName);
|
||||
}
|
||||
|
||||
public async Task UpdateTableAsync(UpdateTableRequest request)
|
||||
{
|
||||
await UpdateTablePgSqlAsync(request);
|
||||
}
|
||||
|
||||
public async Task DeleteTableAsync(string tableName)
|
||||
{
|
||||
var dropPg = $"DROP TABLE \"{tableName.Replace("\"", "\"\"")}\";";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropPg);
|
||||
}
|
||||
|
||||
private static string NormalizePgSqlDefault(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||
var s = raw.Trim();
|
||||
if (s.StartsWith("'") && s.Contains("'::"))
|
||||
{
|
||||
var end = s.IndexOf("'::", StringComparison.Ordinal);
|
||||
if (end > 0)
|
||||
{
|
||||
var inner = s[1..end].Replace("''", "'");
|
||||
return inner;
|
||||
}
|
||||
}
|
||||
if (s.StartsWith("nextval(")) return null;
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string FormatDefaultForPgSql(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
var v = value.Trim();
|
||||
var vLower = v.ToLower();
|
||||
if (vLower is "current_timestamp" or "now()" or "localtimestamp")
|
||||
return $" DEFAULT {v}";
|
||||
if (long.TryParse(v, out _) || decimal.TryParse(v, out _))
|
||||
return $" DEFAULT {v}";
|
||||
if (vLower == "true" || vLower == "1") return " DEFAULT true";
|
||||
if (vLower == "false" || vLower == "0") return " DEFAULT false";
|
||||
return $" DEFAULT '{v.Replace("'", "''")}'";
|
||||
}
|
||||
|
||||
// PGSql 类型映射
|
||||
private static string BuildPgSqlColumnType(TableColumnDto column)
|
||||
{
|
||||
var dt = (column.DataType ?? string.Empty).Trim();
|
||||
var dtLower = dt.ToLower();
|
||||
|
||||
return dtLower switch
|
||||
{
|
||||
"nvarchar" or "varchar" or "character varying" => $"VARCHAR({(column.Length is > 0 ? column.Length : 255)})",
|
||||
"char" or "nchar" => $"CHAR({(column.Length is > 0 ? column.Length : 1)})",
|
||||
"nvarchar(max)" or "varchar(max)" or "text" or "ntext" => "TEXT",
|
||||
"int" or "integer" => "INTEGER",
|
||||
"bigint" => "BIGINT",
|
||||
"tinyint" or "bit" => "SMALLINT",
|
||||
"decimal" or "numeric" =>
|
||||
$"NUMERIC({(column.Length ?? 18)},{(column.Scale ?? 0)})",
|
||||
"float" or "double" => "DOUBLE PRECISION",
|
||||
"datetime" => "TIMESTAMP",
|
||||
"date" => "DATE",
|
||||
"uniqueidentifier" or "uuid" => "UUID",
|
||||
_ => dt
|
||||
};
|
||||
}
|
||||
|
||||
// PGSql 专用:建表
|
||||
private async Task CreateTablePgSqlAsync(CreateTableRequest request)
|
||||
{
|
||||
var columnDefs = new List<string>();
|
||||
foreach (var column in request.Columns.OrderBy(c => c.Order))
|
||||
{
|
||||
var typeSql = BuildPgSqlColumnType(column);
|
||||
|
||||
var isIntLike = typeSql.StartsWith("INTEGER", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("INT4", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("INT8", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("INT4", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("INT2", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("BIGINT", StringComparison.OrdinalIgnoreCase);
|
||||
var wantIdentity = column.IsIdentity && column.IsPrimaryKey && isIntLike;
|
||||
|
||||
// PGSql:自增主键使用 GENERATED BY DEFAULT AS IDENTITY
|
||||
var identitySql = wantIdentity ? " GENERATED ALWAYS AS IDENTITY " : string.Empty;
|
||||
|
||||
var nullSql = column.IsNullable && !column.IsPrimaryKey
|
||||
? "NULL"
|
||||
: "NOT NULL";
|
||||
|
||||
var defaultSql = FormatDefaultForPgSql(column.DefaultValue);
|
||||
|
||||
columnDefs.Add($"\"{column.ColumnName}\" {typeSql}{identitySql} {nullSql}{defaultSql}");
|
||||
}
|
||||
|
||||
var pkCols = request.Columns.Where(c => c.IsPrimaryKey)
|
||||
.OrderBy(c => c.Order)
|
||||
.Select(c => $"\"{c.ColumnName}\"");
|
||||
var pkSql = pkCols.Any() ? $", CONSTRAINT \"PK_{request.TableName}\" PRIMARY KEY ({string.Join(", ", pkCols)})" : string.Empty;
|
||||
|
||||
var createSql = $"CREATE TABLE \"{request.TableName}\" (\n {string.Join(",\n ", columnDefs)}{pkSql}\n);";
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createSql);
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
var commentSql = $"COMMENT ON COLUMN \"{request.TableName}\".\"{column.ColumnName}\" IS '{column.Comment!.Replace("'", "''")}';";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
}
|
||||
|
||||
// PGSql 专用:获取表结构
|
||||
private async Task<TableInfoDto> GetTableInfoPgSqlAsync(string tableName)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT c.column_name, c.data_type, c.character_maximum_length, c.is_nullable, c.ordinal_position,
|
||||
c.numeric_scale, c.numeric_precision,
|
||||
CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary_key,
|
||||
CASE WHEN c.column_default LIKE 'nextval(%' THEN 1 ELSE 0 END AS is_identity,
|
||||
col_description(format('%I.%I', c.table_schema, c.table_name)::regclass::oid, a.attnum) AS comment,
|
||||
c.column_default
|
||||
FROM information_schema.columns c
|
||||
LEFT JOIN (SELECT kcu.table_schema, kcu.table_name, kcu.column_name
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
WHERE tc.constraint_type = 'PRIMARY KEY') pk
|
||||
ON pk.table_schema = c.table_schema AND pk.table_name = c.table_name AND pk.column_name = c.column_name
|
||||
JOIN pg_attribute a ON a.attname = c.column_name AND a.attrelid = format('%I.%I', c.table_schema, c.table_name)::regclass
|
||||
WHERE c.table_schema = @schemaName AND c.table_name = @tableName ORDER BY c.ordinal_position;";
|
||||
|
||||
var dt = await _context.SqlSugarClient.Ado.GetDataTableAsync(sql, new { schemaName = _schema, tableName });
|
||||
var columns = new List<TableColumnDto>();
|
||||
foreach (DataRow row in dt.Rows)
|
||||
{
|
||||
var dataType = row[1]?.ToString() ?? "";
|
||||
var isDecimal = dataType.Equals("numeric", StringComparison.OrdinalIgnoreCase) || dataType.Equals("decimal", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
int? charLen = null;
|
||||
if (row[2] != DBNull.Value && row[2] != null)
|
||||
{
|
||||
var raw = Convert.ToInt64(row[2]);
|
||||
if (raw > 0 && raw <= int.MaxValue) charLen = (int)raw;
|
||||
}
|
||||
|
||||
var numericScale = row[5] == DBNull.Value || row[5] == null ? (int?)null : Convert.ToInt32(row[5]);
|
||||
var numericPrecision = row[6] == DBNull.Value || row[6] == null ? (int?)null : Convert.ToInt32(row[6]);
|
||||
var colDefault = row[10] == DBNull.Value || row[10] == null ? null : NormalizePgSqlDefault(row[10]?.ToString());
|
||||
|
||||
columns.Add(new TableColumnDto
|
||||
{
|
||||
ColumnName = row[0]?.ToString() ?? "",
|
||||
DataType = dataType,
|
||||
Length = isDecimal ? numericPrecision : charLen,
|
||||
Scale = isDecimal ? numericScale : null,
|
||||
IsNullable = string.Equals(row[3]?.ToString(), "YES", StringComparison.OrdinalIgnoreCase),
|
||||
Order = Convert.ToInt32(row[4]),
|
||||
IsPrimaryKey = row[7] != DBNull.Value && row[7] != null && Convert.ToInt32(row[7]) == 1,
|
||||
IsIdentity = row[8] != DBNull.Value && row[8] != null && Convert.ToInt32(row[8]) == 1,
|
||||
Comment = row[9]?.ToString(),
|
||||
DefaultValue = colDefault ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return new TableInfoDto { TableName = tableName, Columns = columns };
|
||||
}
|
||||
|
||||
// PGSql 专用:更新表(新增/删除/修改列 + 主键 & 注释)
|
||||
private async Task UpdateTablePgSqlAsync(UpdateTableRequest request)
|
||||
{
|
||||
var existing = await GetTableInfoPgSqlAsync(request.TableName) ?? throw new InvalidOperationException("Table not found.");
|
||||
var existingByName = existing.Columns.ToDictionary(c => c.ColumnName, c => c, StringComparer.Ordinal);
|
||||
var newNames = request.Columns.Select(c => c.ColumnName).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// 1) 识别并执行重命名字段(区分大小写)
|
||||
var renames = request.Columns
|
||||
.Where(c => c.IsDbField && !string.IsNullOrWhiteSpace(c.OriginalColumnName) &&
|
||||
c.OriginalColumnName != c.ColumnName &&
|
||||
existingByName.Keys.Any(k => string.Equals(k, c.OriginalColumnName, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var r in renames)
|
||||
{
|
||||
var actualKey = existingByName.Keys.FirstOrDefault(k => string.Equals(k, r.OriginalColumnName, StringComparison.OrdinalIgnoreCase));
|
||||
if (actualKey == null) continue;
|
||||
var renameSql = $"ALTER TABLE \"{request.TableName}\" RENAME COLUMN \"{actualKey.Replace("\"", "\"\"")}\" TO \"{r.ColumnName.Replace("\"", "\"\"")}\";";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(renameSql);
|
||||
var oldCol = existingByName[actualKey];
|
||||
existingByName.Remove(actualKey);
|
||||
existingByName[r.ColumnName] = new TableColumnDto { ColumnName = r.ColumnName, DataType = oldCol.DataType, Length = oldCol.Length, Scale = oldCol.Scale, IsNullable = oldCol.IsNullable, IsPrimaryKey = oldCol.IsPrimaryKey, IsIdentity = oldCol.IsIdentity, Comment = oldCol.Comment, DefaultValue = oldCol.DefaultValue ?? "", Order = oldCol.Order };
|
||||
}
|
||||
|
||||
var existingNames = existingByName.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
var columnsToDrop = existingNames.Except(newNames, StringComparer.Ordinal).ToList();
|
||||
|
||||
// 2) 新增列
|
||||
foreach (var column in request.Columns.Where(c => !existingByName.ContainsKey(c.ColumnName)).OrderBy(c => c.Order))
|
||||
{
|
||||
var typeSql = BuildPgSqlColumnType(column);
|
||||
var isIdentity = column.IsIdentity &&
|
||||
(typeSql.StartsWith("INTEGER", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("BIGINT", StringComparison.OrdinalIgnoreCase));
|
||||
var identitySql = isIdentity ? " GENERATED ALWAYS AS IDENTITY" : string.Empty;
|
||||
var nullSql = column.IsNullable && !column.IsPrimaryKey
|
||||
? "NULL"
|
||||
: "NOT NULL";
|
||||
|
||||
var defaultSql = FormatDefaultForPgSql(column.DefaultValue);
|
||||
|
||||
var addColSql = $"ALTER TABLE \"{request.TableName}\" ADD COLUMN \"{column.ColumnName}\" {typeSql}{identitySql} {nullSql}{defaultSql};";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addColSql);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(column.Comment))
|
||||
{
|
||||
var commentSql = $"COMMENT ON COLUMN \"{request.TableName}\".\"{column.ColumnName}\" IS '{column.Comment!.Replace("'", "''")}';";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 修改已有列:仅当有变更时才执行 ALTER
|
||||
foreach (var column in request.Columns.Where(c => existingByName.ContainsKey(c.ColumnName)))
|
||||
{
|
||||
var typeSql = BuildPgSqlColumnType(column);
|
||||
var old = existingByName[column.ColumnName];
|
||||
var isIntegerLike = typeSql.StartsWith("INTEGER", StringComparison.OrdinalIgnoreCase) ||
|
||||
typeSql.StartsWith("BIGINT", StringComparison.OrdinalIgnoreCase);
|
||||
var wantIdentity = column.IsIdentity && column.IsPrimaryKey && isIntegerLike;
|
||||
|
||||
var oldTypeSql = BuildPgSqlColumnType(old);
|
||||
var typeChanged = !string.Equals(oldTypeSql, typeSql, StringComparison.OrdinalIgnoreCase);
|
||||
var nullableChanged = old.IsNullable != column.IsNullable;
|
||||
var defaultChanged = (old.DefaultValue ?? "").Trim() != (column.DefaultValue ?? "").Trim();
|
||||
var identityChanged = isIntegerLike && old.IsIdentity != wantIdentity;
|
||||
var commentChanged = (old.Comment ?? "").Trim() != (column.Comment ?? "").Trim();
|
||||
|
||||
if (typeChanged)
|
||||
{
|
||||
var alterTypeSql = $"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" TYPE {typeSql};";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(alterTypeSql);
|
||||
}
|
||||
|
||||
if (nullableChanged)
|
||||
{
|
||||
var nullSql = column.IsNullable && !column.IsPrimaryKey ? "DROP NOT NULL" : "SET NOT NULL";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" {nullSql};");
|
||||
}
|
||||
|
||||
if (defaultChanged)
|
||||
{
|
||||
var defaultSql = FormatDefaultForPgSql(column.DefaultValue);
|
||||
if (!string.IsNullOrWhiteSpace(defaultSql))
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" SET {defaultSql.Trim()};");
|
||||
else
|
||||
try { await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" DROP DEFAULT;"); } catch { }
|
||||
}
|
||||
|
||||
if (identityChanged)
|
||||
{
|
||||
if (wantIdentity && !old.IsIdentity)
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" ADD GENERATED BY DEFAULT AS IDENTITY;");
|
||||
else if (!wantIdentity && old.IsIdentity)
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" ALTER COLUMN \"{column.ColumnName}\" DROP IDENTITY IF EXISTS;");
|
||||
}
|
||||
|
||||
if (commentChanged)
|
||||
{
|
||||
var commentText = string.IsNullOrWhiteSpace(column.Comment) ? "NULL" : $"'{column.Comment!.Replace("'", "''")}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"COMMENT ON COLUMN \"{request.TableName}\".\"{column.ColumnName}\" IS {commentText};");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var colName in columnsToDrop)
|
||||
{
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{request.TableName}\" DROP COLUMN \"{colName}\";");
|
||||
}
|
||||
|
||||
var newPkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var existingPkCols = existingByName.Values.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var pkChanged = !newPkCols.SequenceEqual(existingPkCols);
|
||||
|
||||
if (pkChanged)
|
||||
{
|
||||
const string findPkSql = "SELECT c.conname FROM pg_constraint c JOIN pg_class t ON c.conrelid = t.oid JOIN pg_namespace n ON n.oid = t.relnamespace WHERE c.contype = 'p' AND n.nspname = @schemaName AND t.relname = @tableName;";
|
||||
var pkDt = await _context.SqlSugarClient.Ado.GetDataTableAsync(findPkSql, new { schemaName = _schema, tableName = request.TableName });
|
||||
foreach (DataRow r in pkDt.Rows)
|
||||
{
|
||||
var pkName = r[0]?.ToString();
|
||||
if (!string.IsNullOrEmpty(pkName))
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{_schema}\".\"{request.TableName}\" DROP CONSTRAINT \"{pkName}\";");
|
||||
}
|
||||
|
||||
if (newPkCols.Any())
|
||||
{
|
||||
var pkColsSql = string.Join(", ", newPkCols.Select(c => $"\"{c}\""));
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"ALTER TABLE \"{_schema}\".\"{request.TableName}\" ADD CONSTRAINT \"PK_{request.TableName}\" PRIMARY KEY ({pkColsSql});");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.EFDbContext;
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class SqlServerTableProvider : ITableDatabaseProvider
|
||||
{
|
||||
private readonly BaseDbContext _context;
|
||||
|
||||
public SqlServerTableProvider(BaseDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private static string NormalizeSqlServerDefault(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||
var s = raw.Trim();
|
||||
while (s.StartsWith("(") && s.EndsWith(")")) s = s[1..^1].Trim();
|
||||
return string.IsNullOrWhiteSpace(s) ? null : s;
|
||||
}
|
||||
|
||||
private static string FormatDefaultForSqlServer(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
var v = value.Trim();
|
||||
var vLower = v.ToLower();
|
||||
if (vLower is "getdate()" or "newid()" or "sysdatetime()")
|
||||
return $" DEFAULT {v}";
|
||||
if (long.TryParse(v, out _) || decimal.TryParse(v, out _))
|
||||
return $" DEFAULT {v}";
|
||||
if (vLower == "true" || vLower == "1") return " DEFAULT 1";
|
||||
if (vLower == "false" || vLower == "0") return " DEFAULT 0";
|
||||
return $" DEFAULT N'{v.Replace("'", "''")}'";
|
||||
}
|
||||
|
||||
private static string BuildColumnDefinition(TableColumnDto column)
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
var dtLower = (column.DataType ?? "").Trim().ToLower();
|
||||
if (dtLower == "nvarchar(max)") { dtLower = "nvarchar"; }
|
||||
if (dtLower == "varchar(max)") { dtLower = "varchar"; }
|
||||
if (dtLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var length = (column.DataType ?? "").Contains("max", StringComparison.OrdinalIgnoreCase) ? -1 : (column.Length ?? 255);
|
||||
definition += $"{dtLower}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLower is "char" or "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLower is "decimal" or "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType ?? "nvarchar";
|
||||
}
|
||||
if (column.IsIdentity) definition += " IDENTITY(1,1)";
|
||||
if (!column.IsNullable) definition += " NOT NULL";
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
return definition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Server 不支持 ALTER COLUMN 修改 IDENTITY,通过临时表重建实现
|
||||
/// </summary>
|
||||
private async Task RebuildTableForIdentityChangeAsync(UpdateTableRequest request, Dictionary<string, TableColumnDto> existingByName)
|
||||
{
|
||||
var tableName = request.TableName;
|
||||
var tmpName = $"Tmp_{tableName}_{Guid.NewGuid().ToString("N")[..8]}";
|
||||
var colDefs = request.Columns.OrderBy(c => c.Order).Select(BuildColumnDefinition).ToList();
|
||||
var createTmpSql = $"CREATE TABLE [{tmpName}] (\n " + string.Join(",\n ", colDefs) + "\n)";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createTmpSql);
|
||||
|
||||
var colsToCopy = request.Columns.Where(c => existingByName.ContainsKey(c.ColumnName)).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var hasIdentity = request.Columns.Any(c => c.IsIdentity);
|
||||
var colList = string.Join(", ", colsToCopy.Select(c => $"[{c}]"));
|
||||
|
||||
// IDENTITY_INSERT 是会话级,必须与 INSERT 在同一连接/同一批中执行
|
||||
if (colsToCopy.Count > 0)
|
||||
{
|
||||
var insertBatch = hasIdentity
|
||||
? $"SET IDENTITY_INSERT [{tmpName}] ON; INSERT INTO [{tmpName}] ({colList}) SELECT {colList} FROM [{tableName}]; SET IDENTITY_INSERT [{tmpName}] OFF"
|
||||
: $"INSERT INTO [{tmpName}] ({colList}) SELECT {colList} FROM [{tableName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(insertBatch);
|
||||
}
|
||||
|
||||
var dropPkSql = $@"
|
||||
DECLARE @pk NVARCHAR(200);
|
||||
SELECT @pk = name FROM sys.key_constraints WHERE type = 'PK' AND parent_object_id = OBJECT_ID('{tableName.Replace("'", "''")}');
|
||||
IF @pk IS NOT NULL EXEC('ALTER TABLE [{tableName}] DROP CONSTRAINT [' + @pk + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropPkSql);
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"DROP TABLE [{tableName}]");
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"EXEC sp_rename 'dbo.[{tmpName}]', '{tableName.Replace("'", "''")}', 'OBJECT'");
|
||||
|
||||
if (request.Columns.Any(c => c.IsPrimaryKey))
|
||||
{
|
||||
var pkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => $"[{c.ColumnName}]");
|
||||
var addPkSql = $"ALTER TABLE [{tableName}] ADD CONSTRAINT [PK_{tableName}] PRIMARY KEY ({string.Join(", ", pkCols)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addPkSql);
|
||||
}
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'{tableName.Replace("'", "''")}', @level2type = N'COLUMN', @level2name = N'{column.ColumnName.Replace("'", "''")}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
catch { /* 注释已存在时忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TableExistsAsync(string tableName)
|
||||
{
|
||||
const string sql = "SELECT COUNT(*) as Value FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName";
|
||||
var res = await _context.SqlSugarClient.Ado.GetScalarAsync(sql, new { tableName });
|
||||
return Convert.ToInt32(res) > 0;
|
||||
}
|
||||
|
||||
public async Task CreateTableAsync(CreateTableRequest request)
|
||||
{
|
||||
var columnDefinitions = new List<string>();
|
||||
|
||||
foreach (var column in request.Columns.OrderBy(c => c.Order))
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
|
||||
// Data type and length
|
||||
var dtLower = column.DataType.ToLower();
|
||||
if (dtLower == "nvarchar" || dtLower == "varchar")
|
||||
{
|
||||
var length = column.Length ?? 255;
|
||||
definition += $"{column.DataType}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLower == "char" || dtLower == "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLower == "decimal" || dtLower == "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType;
|
||||
}
|
||||
|
||||
// Identity (Auto increment)
|
||||
if (column.IsIdentity)
|
||||
{
|
||||
definition += " IDENTITY(1,1)";
|
||||
}
|
||||
|
||||
// Nullable
|
||||
if (!column.IsNullable)
|
||||
{
|
||||
definition += " NOT NULL";
|
||||
}
|
||||
|
||||
// Default value
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
|
||||
columnDefinitions.Add(definition);
|
||||
}
|
||||
|
||||
// Build CREATE TABLE statement
|
||||
var createTableSql = $"CREATE TABLE [{request.TableName}] (\n " +
|
||||
string.Join(",\n ", columnDefinitions) +
|
||||
"\n)";
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createTableSql);
|
||||
|
||||
// Add primary key constraint
|
||||
var pkColumns = request.Columns
|
||||
.Where(c => c.IsPrimaryKey)
|
||||
.OrderBy(c => c.Order)
|
||||
.Select(c => $"[{c.ColumnName}]");
|
||||
|
||||
var pkConstraintName = $"PK_{request.TableName}";
|
||||
var pkSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{pkConstraintName}] PRIMARY KEY ({string.Join(", ", pkColumns)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(pkSql);
|
||||
|
||||
// Add column comments (MS_Description)
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{request.TableName}',
|
||||
@level2type = N'COLUMN', @level2name = N'{column.ColumnName}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAllTablesAsync()
|
||||
{
|
||||
const string sql = "SELECT TABLE_NAME as Value FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME";
|
||||
var rows = await _context.SqlSugarClient.Ado.SqlQueryAsync<string>(sql);
|
||||
return rows ?? new List<string>();
|
||||
}
|
||||
|
||||
public async Task<TableInfoDto> GetTableInfoAsync(string tableName)
|
||||
{
|
||||
const string columnsSql = @"
|
||||
DECLARE @objId INT = OBJECT_ID(QUOTENAME('dbo') + '.' + QUOTENAME(@tableName));
|
||||
SELECT
|
||||
c.COLUMN_NAME,
|
||||
c.DATA_TYPE,
|
||||
c.CHARACTER_MAXIMUM_LENGTH,
|
||||
c.IS_NULLABLE,
|
||||
c.ORDINAL_POSITION,
|
||||
c.NUMERIC_SCALE,
|
||||
c.NUMERIC_PRECISION,
|
||||
COLUMNPROPERTY(@objId, c.COLUMN_NAME, 'IsIdentity') AS IS_IDENTITY,
|
||||
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IS_PRIMARY_KEY,
|
||||
CAST(ep.value AS NVARCHAR(4000)) AS COMMENT,
|
||||
c.COLUMN_DEFAULT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN (
|
||||
SELECT ku.TABLE_NAME, ku.COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
||||
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
|
||||
ON tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
|
||||
) pk ON c.TABLE_NAME = pk.TABLE_NAME AND c.COLUMN_NAME = pk.COLUMN_NAME
|
||||
LEFT JOIN sys.columns sc ON sc.object_id = @objId AND sc.name = c.COLUMN_NAME
|
||||
LEFT JOIN sys.extended_properties ep ON ep.major_id = sc.object_id AND ep.minor_id = sc.column_id AND ep.name = 'MS_Description'
|
||||
WHERE c.TABLE_SCHEMA = 'dbo' AND c.TABLE_NAME = @tableName
|
||||
ORDER BY c.ORDINAL_POSITION;";
|
||||
|
||||
var dt = await _context.SqlSugarClient.Ado.GetDataTableAsync(columnsSql, new { tableName });
|
||||
var columnsWithComments = new List<TableColumnDto>();
|
||||
foreach (DataRow row in dt.Rows)
|
||||
{
|
||||
var length = row[2] == DBNull.Value || row[2] == null ? (int?)null : Convert.ToInt32(row[2]);
|
||||
var numericScale = row[5] == DBNull.Value || row[5] == null ? (int?)null : Convert.ToInt32(row[5]);
|
||||
var numericPrecision = row[6] == DBNull.Value || row[6] == null ? (int?)null : Convert.ToInt32(row[6]);
|
||||
var dataType = row[1]?.ToString() ?? "";
|
||||
var isDecimal = dataType.Equals("decimal", StringComparison.OrdinalIgnoreCase) || dataType.Equals("numeric", StringComparison.OrdinalIgnoreCase);
|
||||
var colDefault = row[10] == DBNull.Value || row[10] == null ? null : NormalizeSqlServerDefault(row[10]?.ToString());
|
||||
|
||||
columnsWithComments.Add(new TableColumnDto
|
||||
{
|
||||
ColumnName = row[0]?.ToString() ?? "",
|
||||
DataType = dataType,
|
||||
Length = isDecimal ? numericPrecision : (length == -1 ? -1 : length),
|
||||
Scale = isDecimal ? numericScale : null,
|
||||
IsNullable = row[3]?.ToString() == "YES",
|
||||
Order = Convert.ToInt32(row[4]),
|
||||
IsIdentity = row[7] != DBNull.Value && row[7] != null && Convert.ToInt32(row[7]) == 1,
|
||||
IsPrimaryKey = row[8] != DBNull.Value && row[8] != null && Convert.ToInt32(row[8]) == 1,
|
||||
Comment = row[9]?.ToString(),
|
||||
DefaultValue = colDefault ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return new TableInfoDto { TableName = tableName, Columns = columnsWithComments };
|
||||
}
|
||||
|
||||
public async Task UpdateTableAsync(UpdateTableRequest request)
|
||||
{
|
||||
var existingTable = await GetTableInfoAsync(request.TableName);
|
||||
if (existingTable == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not retrieve table information for '{request.TableName}'.");
|
||||
}
|
||||
|
||||
var existingByName = existingTable.Columns.ToDictionary(c => c.ColumnName, c => c, StringComparer.Ordinal);
|
||||
var newColumnNames = request.Columns.Select(c => c.ColumnName).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// 1) 识别并执行重命名字段(区分大小写)
|
||||
var renames = request.Columns
|
||||
.Where(c => c.IsDbField && !string.IsNullOrWhiteSpace(c.OriginalColumnName) &&
|
||||
c.OriginalColumnName != c.ColumnName &&
|
||||
existingByName.Keys.Any(k => string.Equals(k, c.OriginalColumnName, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var r in renames)
|
||||
{
|
||||
var actualKey = existingByName.Keys.FirstOrDefault(k => string.Equals(k, r.OriginalColumnName, StringComparison.OrdinalIgnoreCase));
|
||||
if (actualKey == null) continue;
|
||||
var renameSql = $"EXEC sp_rename 'dbo.[{request.TableName}].[{actualKey}]', '{r.ColumnName.Replace("'", "''")}', 'COLUMN';";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(renameSql);
|
||||
var oldCol = existingByName[actualKey];
|
||||
existingByName.Remove(actualKey);
|
||||
existingByName[r.ColumnName] = new TableColumnDto { ColumnName = r.ColumnName, DataType = oldCol.DataType, Length = oldCol.Length, Scale = oldCol.Scale, IsNullable = oldCol.IsNullable, IsPrimaryKey = oldCol.IsPrimaryKey, IsIdentity = oldCol.IsIdentity, Comment = oldCol.Comment, DefaultValue = oldCol.DefaultValue ?? "", Order = oldCol.Order };
|
||||
}
|
||||
|
||||
var existingColumnNames = existingByName.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
var columnsToDrop = existingColumnNames.Except(newColumnNames);
|
||||
foreach (var columnName in columnsToDrop)
|
||||
{
|
||||
var dropColumnSql = $"ALTER TABLE [{request.TableName}] DROP COLUMN [{columnName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropColumnSql);
|
||||
}
|
||||
|
||||
// 2) 新增列
|
||||
var columnsToAdd = request.Columns.Where(c => !existingColumnNames.Contains(c.ColumnName, StringComparer.Ordinal));
|
||||
foreach (var column in columnsToAdd.OrderBy(c => c.Order))
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
var dtLowerAdd = column.DataType.ToLower();
|
||||
|
||||
if (dtLowerAdd == "nvarchar" || dtLowerAdd == "varchar")
|
||||
{
|
||||
var length = column.Length ?? 255;
|
||||
definition += $"{column.DataType}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLowerAdd == "char" || dtLowerAdd == "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLowerAdd == "decimal" || dtLowerAdd == "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType;
|
||||
}
|
||||
|
||||
if (column.IsIdentity)
|
||||
{
|
||||
definition += " IDENTITY(1,1)";
|
||||
}
|
||||
|
||||
if (!column.IsNullable)
|
||||
{
|
||||
definition += " NOT NULL";
|
||||
}
|
||||
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
|
||||
var addColumnSql = $"ALTER TABLE [{request.TableName}] ADD {definition}";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addColumnSql);
|
||||
// 新列注释
|
||||
if (!string.IsNullOrWhiteSpace(column.Comment))
|
||||
{
|
||||
try
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{request.TableName.Replace("'", "''")}',
|
||||
@level2type = N'COLUMN', @level2name = N'{column.ColumnName.Replace("'", "''")}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
catch { /* 注释已存在时忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Alter existing columns: 类型/长度/小数位/可空(existingByName 已在 rename 后更新)
|
||||
var columnsToAlter = request.Columns
|
||||
.Where(c => existingByName.ContainsKey(c.ColumnName))
|
||||
.OrderBy(c => c.Order);
|
||||
|
||||
foreach (var column in columnsToAlter)
|
||||
{
|
||||
var existing = existingByName[column.ColumnName];
|
||||
|
||||
var defaultChanged = (existing.DefaultValue ?? "").Trim() != (column.DefaultValue ?? "").Trim();
|
||||
|
||||
var newTypeRaw = column.DataType?.Trim() ?? "";
|
||||
var newTypeLower = newTypeRaw.ToLower();
|
||||
var oldTypeLower = (existing.DataType ?? "").Trim().ToLower();
|
||||
|
||||
// 兼容传入 nvarchar(max)/varchar(max)
|
||||
if (newTypeLower == "nvarchar(max)") { newTypeLower = "nvarchar"; column.Length = -1; }
|
||||
if (newTypeLower == "varchar(max)") { newTypeLower = "varchar"; column.Length = -1; }
|
||||
|
||||
bool typeChanged = !string.Equals(oldTypeLower, newTypeLower, StringComparison.OrdinalIgnoreCase);
|
||||
bool nullableChanged = existing.IsNullable != column.IsNullable;
|
||||
|
||||
bool lengthChanged = false;
|
||||
bool scaleChanged = false;
|
||||
if (newTypeLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var oldLen = existing.Length ?? 255;
|
||||
var newLen = column.Length ?? 255;
|
||||
lengthChanged = oldLen != newLen;
|
||||
}
|
||||
else if (newTypeLower is "char" or "nchar")
|
||||
{
|
||||
var oldLen = existing.Length ?? 1;
|
||||
var newLen = column.Length ?? 1;
|
||||
lengthChanged = oldLen != newLen;
|
||||
}
|
||||
else if (newTypeLower is "decimal" or "numeric")
|
||||
{
|
||||
var oldPrec = existing.Length ?? 18;
|
||||
var newPrec = column.Length ?? 18;
|
||||
lengthChanged = oldPrec != newPrec;
|
||||
|
||||
var oldScale = existing.Scale ?? 0;
|
||||
var newScale = column.Scale ?? 0;
|
||||
scaleChanged = oldScale != newScale;
|
||||
}
|
||||
|
||||
bool isIdentityChanged = existing.IsIdentity != column.IsIdentity;
|
||||
|
||||
// 仅当字段有实际修改时才执行更新脚本
|
||||
if (!typeChanged && !nullableChanged && !lengthChanged && !scaleChanged && !defaultChanged && !isIdentityChanged)
|
||||
continue;
|
||||
|
||||
// IsIdentity 变更:SQL Server 不支持 ALTER COLUMN 修改 IDENTITY,需通过临时表重建
|
||||
if (isIdentityChanged)
|
||||
{
|
||||
await RebuildTableForIdentityChangeAsync(request, existingByName);
|
||||
var refreshed = await GetTableInfoAsync(request.TableName);
|
||||
foreach (var c in refreshed.Columns)
|
||||
existingByName[c.ColumnName] = c;
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeChanged || nullableChanged || lengthChanged || scaleChanged)
|
||||
{
|
||||
string typeSql;
|
||||
if (newTypeLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var len = column.Length ?? 255;
|
||||
typeSql = $"{newTypeLower}({(len == -1 ? "MAX" : len.ToString())})";
|
||||
}
|
||||
else if (newTypeLower is "char" or "nchar")
|
||||
{
|
||||
var len = column.Length ?? 1;
|
||||
typeSql = $"{newTypeLower}({len})";
|
||||
}
|
||||
else if (newTypeLower is "decimal" or "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
typeSql = $"{newTypeLower}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeSql = newTypeRaw;
|
||||
}
|
||||
|
||||
var nullSql = column.IsNullable ? "NULL" : "NOT NULL";
|
||||
var alterSql = $"ALTER TABLE [{request.TableName}] ALTER COLUMN [{column.ColumnName}] {typeSql} {nullSql}";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(alterSql);
|
||||
}
|
||||
|
||||
if (defaultChanged)
|
||||
{
|
||||
var dfName = $"DF_{request.TableName}_{column.ColumnName}";
|
||||
var dropDfSql = $@"DECLARE @cn NVARCHAR(200);
|
||||
SELECT @cn = dc.name FROM sys.default_constraints dc
|
||||
INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
|
||||
WHERE c.object_id = OBJECT_ID('{request.TableName.Replace("'", "''")}') AND c.name = N'{column.ColumnName.Replace("'", "''")}';
|
||||
IF @cn IS NOT NULL EXEC('ALTER TABLE [{request.TableName}] DROP CONSTRAINT [' + @cn + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropDfSql);
|
||||
if (!string.IsNullOrWhiteSpace(column.DefaultValue))
|
||||
{
|
||||
var defVal = column.DefaultValue.Trim();
|
||||
var vLower = defVal.ToLower();
|
||||
var innerExpr = vLower is "getdate()" or "newid()" or "sysdatetime()" ? defVal
|
||||
: long.TryParse(defVal, out _) || decimal.TryParse(defVal, out _) ? defVal
|
||||
: vLower == "true" || vLower == "1" ? "1"
|
||||
: vLower == "false" || vLower == "0" ? "0"
|
||||
: $"N'{defVal.Replace("'", "''")}'";
|
||||
var addDfSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{dfName}] DEFAULT ({innerExpr}) FOR [{column.ColumnName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addDfSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingPkCols = existingTable.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var newPkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var pkChanged = !existingPkCols.SequenceEqual(newPkCols);
|
||||
|
||||
if (pkChanged)
|
||||
{
|
||||
// Drop existing PK constraint
|
||||
var dropPkSql = $@"
|
||||
DECLARE @pkConstraintName NVARCHAR(200);
|
||||
SELECT @pkConstraintName = name
|
||||
FROM sys.key_constraints
|
||||
WHERE type = 'PK' AND parent_object_id = OBJECT_ID('{request.TableName}');
|
||||
IF @pkConstraintName IS NOT NULL
|
||||
EXEC('ALTER TABLE [{request.TableName}] DROP CONSTRAINT [' + @pkConstraintName + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropPkSql);
|
||||
|
||||
if (newPkCols.Any())
|
||||
{
|
||||
var pkConstraintName = $"PK_{request.TableName}";
|
||||
var pkColumns = newPkCols.Select(c => $"[{c}]");
|
||||
|
||||
var addPkSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{pkConstraintName}] PRIMARY KEY ({string.Join(", ", pkColumns)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addPkSql);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
if (existingByName.TryGetValue(column.ColumnName, out var ec) && (ec.Comment ?? "").Trim() == (column.Comment ?? "").Trim())
|
||||
continue;
|
||||
|
||||
var escapedComment = column.Comment!.Replace("'", "''");
|
||||
var tableNameEscaped = request.TableName.Replace("'", "''");
|
||||
var columnNameEscaped = column.ColumnName.Replace("'", "''");
|
||||
try
|
||||
{
|
||||
var updateCommentSql = $@"
|
||||
EXEC sp_updateextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{escapedComment}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{tableNameEscaped}',
|
||||
@level2type = N'COLUMN', @level2name = N'{columnNameEscaped}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(updateCommentSql);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
var addCommentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{escapedComment}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{tableNameEscaped}',
|
||||
@level2type = N'COLUMN', @level2name = N'{columnNameEscaped}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addCommentSql);
|
||||
}
|
||||
catch { /* 忽略 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteTableAsync(string tableName)
|
||||
{
|
||||
var dropTableSql = $"DROP TABLE [{tableName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropTableSql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class TableProviderFactory : ITableProviderFactory, IDependency
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
public TableProviderFactory(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public ITableDatabaseProvider GetProvider(string dbService)
|
||||
{
|
||||
var type = DbRelativeCache.GetDbContextType(dbService);
|
||||
object dbObj = _httpContextAccessor.HttpContext.RequestServices.GetService(type);
|
||||
BaseDbContext _context = dbObj as BaseDbContext;
|
||||
string dbType = DbRelativeCache.GetDbType(dbService);
|
||||
switch (dbType)
|
||||
{
|
||||
case "MySql":
|
||||
return new MySqlTableProvider(_context);
|
||||
case "PgSql":
|
||||
return new PgSqlTableProvider(_context);
|
||||
case "MsSql":
|
||||
return new SqlServerTableProvider(_context);
|
||||
default:
|
||||
throw new Exception("数据库未支持");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
106
api_sqlsugar/VolPro.Builder/Services/DataBase/TableService.cs
Normal file
106
api_sqlsugar/VolPro.Builder/Services/DataBase/TableService.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Builder.Services;
|
||||
using VolPro.Core;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Core.UserManager;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class TableService : ITableService, IDependency
|
||||
{
|
||||
private readonly ITableProviderFactory _factory;
|
||||
WebResponseContent webResponse = new WebResponseContent();
|
||||
public TableService(ITableProviderFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public async Task<bool> TableExistsAsync(string dbService, string tableName)
|
||||
{
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
return await provider.TableExistsAsync(tableName);
|
||||
}
|
||||
|
||||
public async Task<WebResponseContent> CreateTableAsync(string dbService, CreateTableRequest request)
|
||||
{
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
|
||||
if (await provider.TableExistsAsync(request.TableName))
|
||||
{
|
||||
return webResponse.Error($"{request.TableName}表已存在");
|
||||
}
|
||||
await provider.CreateTableAsync(request);
|
||||
return webResponse.OK("创建成功");
|
||||
}
|
||||
public async Task<object> GetAllTablesAsync(string dbService)
|
||||
{
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
var tables = await provider.GetAllTablesAsync();
|
||||
var tableInfo = TableColumnContext.TableInfo;
|
||||
var list = tables.Select(s => new
|
||||
{
|
||||
table = s,
|
||||
name = tableInfo.Where(x => x.TableName == s).Select(x => x.ColumnCNName).FirstOrDefault() ?? s
|
||||
}).OrderBy(item => !IsStartWithLetter(item.name))
|
||||
.ThenBy(item => item.name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
return list;
|
||||
}
|
||||
private static bool IsStartWithLetter(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str)) return false;
|
||||
char firstChar = str[0];
|
||||
return (firstChar >= 'a' && firstChar <= 'z') ||
|
||||
(firstChar >= 'A' && firstChar <= 'Z');
|
||||
}
|
||||
|
||||
public async Task<TableInfoDto> GetTableInfoAsync(string dbService, string tableName)
|
||||
{
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
|
||||
var res= await provider.GetTableInfoAsync(tableName);
|
||||
foreach (var item in res.Columns)
|
||||
{
|
||||
item.IsDbField = true;
|
||||
item.OriginalColumnName = item.ColumnName ?? string.Empty;
|
||||
item.Comment = TableColumnContext.Data.Where(x => x.ColumnName == item.ColumnName).Select(s => s.ColumnCnName).FirstOrDefault() ?? item.Comment;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<WebResponseContent> UpdateTableAsync(string dbService, UpdateTableRequest request)
|
||||
{
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
if (!await provider.TableExistsAsync(request.TableName))
|
||||
{
|
||||
return webResponse.Error($"{request.TableName}不存在");
|
||||
}
|
||||
await provider.UpdateTableAsync(request);
|
||||
return webResponse.OK("修改成功");
|
||||
}
|
||||
|
||||
public async Task<WebResponseContent> DeleteTableAsync(string dbService, string tableName)
|
||||
{
|
||||
|
||||
if (tableName.ToLower().StartsWith("sys"))
|
||||
{
|
||||
return webResponse.Error("系统表不能删除,请在数据库操作".Translator());
|
||||
}
|
||||
var provider = _factory.GetProvider(dbService);
|
||||
|
||||
if (!await provider.TableExistsAsync(tableName)) {
|
||||
return webResponse.Error($"{tableName}不存在");
|
||||
}
|
||||
await provider.DeleteTableAsync(tableName);
|
||||
return webResponse.OK("删除成功");
|
||||
}
|
||||
|
||||
}
|
||||
93
api_sqlsugar/VolPro.Builder/Utility/ProjectPath.cs
Normal file
93
api_sqlsugar/VolPro.Builder/Utility/ProjectPath.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using VolPro.Core.Extensions;
|
||||
|
||||
namespace VolPro.Builder.Utility
|
||||
{
|
||||
public class ProjectPath
|
||||
{
|
||||
// private int findCount = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 获取web父目录所在位置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DirectoryInfo GetProjectDirectoryInfo()
|
||||
{
|
||||
return GetProjectDirectoryInfo(new DirectoryInfo("".MapPath()), 1);
|
||||
}
|
||||
public static string GetProjectFileName(string startsWith)
|
||||
{
|
||||
string fileNames = GetProjectDirectoryInfo()?.GetDirectories()
|
||||
.Where(
|
||||
c =>
|
||||
//c.Name.StartsWith(startsWith)&&
|
||||
c.Name != startsWith + ".Core"
|
||||
&& c.Name != startsWith + ".Entity"
|
||||
&& !c.Name.ToLower().EndsWith(".web")
|
||||
&& !c.Name.ToLower().EndsWith(".webapi")
|
||||
&& !c.Name.ToLower().EndsWith(".builder")
|
||||
&& c.Name.ToLower()!=".vs"
|
||||
).Select(x => x.Name).ToList().Serialize();
|
||||
if (string.IsNullOrEmpty(fileNames))
|
||||
{
|
||||
fileNames = new DirectoryInfo("".MapPath()).GetFiles().Where(x => x.Name.EndsWith(".dll")
|
||||
//&& x.Name.StartsWith(startsWith)
|
||||
&& !x.Name.EndsWith(".Core.dll")
|
||||
&& !x.Name.EndsWith(".Entity.dll")
|
||||
&& !x.Name.EndsWith(".Builder.dll")
|
||||
&& !x.Name.ToLower().EndsWith(".web")
|
||||
&& !x.Name.ToLower().EndsWith(".webapi")
|
||||
&& !x.Name.ToLower().EndsWith(".builder")
|
||||
).Select(x => x.Name.Replace(".dll", "")).ToList().Serialize();
|
||||
}
|
||||
return fileNames ?? "''";
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取指定结尾的项目名称
|
||||
/// </summary>
|
||||
/// <param name="lastIndexOfName"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetLastIndexOfDirectoryName(string lastIndexOfName)
|
||||
{
|
||||
string projectName = GetProjectDirectoryInfo()?.GetDirectories()
|
||||
.Where(c => c.Name.LastIndexOf(lastIndexOfName) != -1).Select(x => x.Name).FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(projectName))
|
||||
{
|
||||
projectName = new DirectoryInfo("".MapPath()).GetFiles().Where(x => x.Name.LastIndexOf(lastIndexOfName + ".dll") != -1).FirstOrDefault().Name;
|
||||
if (!string.IsNullOrEmpty(projectName))
|
||||
{
|
||||
projectName = projectName.Replace(".dll", "");
|
||||
}
|
||||
}
|
||||
return projectName;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取项目所在路径
|
||||
/// </summary>
|
||||
/// <param name="directoryInfo"></param>
|
||||
/// <returns></returns>
|
||||
private static DirectoryInfo GetProjectDirectoryInfo(DirectoryInfo directoryInfo, int findCount)
|
||||
{
|
||||
if (directoryInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (directoryInfo.Exists
|
||||
&& directoryInfo.GetDirectories().Where(x => x.Name.LastIndexOf(".Web") != -1).FirstOrDefault() != null)
|
||||
{
|
||||
return directoryInfo;
|
||||
}
|
||||
if (findCount < 7)
|
||||
{
|
||||
findCount++;
|
||||
DirectoryInfo dir = GetProjectDirectoryInfo(directoryInfo.Parent, findCount);
|
||||
if (dir != null)
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
api_sqlsugar/VolPro.Builder/VolPro.Builder.csproj
Normal file
40
api_sqlsugar/VolPro.Builder/VolPro.Builder.csproj
Normal file
@@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<SccProjectName></SccProjectName>
|
||||
<SccProvider></SccProvider>
|
||||
<SccAuxPath></SccAuxPath>
|
||||
<SccLocalPath></SccLocalPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<AssemblyName>VolPro.Builder</AssemblyName>
|
||||
<RootNamespace>VolPro.Builder</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;NU1902;NU1903;NU1904;</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;NU1902;NU1903;NU1904;</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="8.0.0" />
|
||||
<PackageReference Include="AutoMapper" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VolPro.Core\VolPro.Core.csproj" />
|
||||
<ProjectReference Include="..\VolPro.Entity\VolPro.Entity.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.BackgroundServices.mail;
|
||||
using VolPro.Core.PerformanceMonitor;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices
|
||||
{
|
||||
public static class BackgroundServiceExtensions
|
||||
{
|
||||
public static IServiceCollection AddBackgroundServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
// 注册邮件服务
|
||||
builder.Services.AddSingleton<IMailService, MailService>();
|
||||
// 注册邮件后台服务
|
||||
builder.Services.AddHostedService<MailBackgroundService>();
|
||||
|
||||
return builder.Services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件服务接口
|
||||
/// </summary>
|
||||
public interface IMailService
|
||||
{
|
||||
/// <summary>
|
||||
/// 直接发送邮件(同步发送)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
/// <returns></returns>
|
||||
Task SendAsync(string email, string subject, string body, bool isHtml = true);
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
/// <returns></returns>
|
||||
Task SendQueuedAsync(string email, string subject, string body, bool isHtml = true);
|
||||
|
||||
/// <summary>
|
||||
/// 发送邮件(加入队列,同步方法)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
void Send(string email, string subject, string body, bool isHtml = true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Quartz.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件后台服务,使用Channel队列处理邮件发送
|
||||
/// </summary>
|
||||
public class MailBackgroundService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public MailBackgroundService(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
Console.WriteLine("发送邮件服务已启动");
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从队列中读取邮件请求
|
||||
var mailRequest = await MailService.MailQueue.Reader.ReadAsync(stoppingToken);
|
||||
if (mailRequest.RetryCount > mailRequest.MaxRetryCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var mailService = scope.ServiceProvider.GetRequiredService<IMailService>();
|
||||
|
||||
try
|
||||
{
|
||||
await mailService.SendAsync(mailRequest.MailMessage.To,
|
||||
mailRequest.MailMessage.Subject,
|
||||
mailRequest.MailMessage.Body,
|
||||
mailRequest.MailMessage.IsHtml);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
mailRequest.RetryCount++;
|
||||
Console.WriteLine($"邮件发送异常:{ex.Message + ex.StackTrace}");
|
||||
Core.Services.Logger.AddAsync($"邮件发送异常:{ex.Message + ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ChannelClosedException)
|
||||
{
|
||||
// 队列已关闭,退出循环
|
||||
break;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 取消令牌触发,退出循环
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 邮件队列处理出现未知错误,短暂延迟后继续处理
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 关闭队列写入器
|
||||
MailService.MailQueue.Writer.Complete();
|
||||
|
||||
// 等待所有待处理的邮件完成
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件请求模型,用于队列处理
|
||||
/// </summary>
|
||||
public class MailRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// 邮件消息
|
||||
/// </summary>
|
||||
public required MailRequestMessage MailMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 重试次数
|
||||
/// </summary>
|
||||
public int RetryCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 最大重试次数
|
||||
/// </summary>
|
||||
public int MaxRetryCount { get; set; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 下次重试时间
|
||||
/// </summary>
|
||||
public DateTime? NextRetryTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件消息模型
|
||||
/// </summary>
|
||||
public class MailRequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 收件人地址
|
||||
/// </summary>
|
||||
public string To { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 抄送人地址
|
||||
/// </summary>
|
||||
public string Cc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密送人地址
|
||||
/// </summary>
|
||||
public string Bcc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件主题
|
||||
/// </summary>
|
||||
public required string Subject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件内容
|
||||
/// </summary>
|
||||
public required string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为HTML格式
|
||||
/// </summary>
|
||||
public bool IsHtml { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 附件路径列表
|
||||
/// </summary>
|
||||
public List<string> Attachments { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 优先级
|
||||
/// </summary>
|
||||
public MailPriority Priority { get; set; } = MailPriority.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 邮件优先级
|
||||
/// </summary>
|
||||
public enum MailPriority
|
||||
{
|
||||
Low = 0,
|
||||
Normal = 1,
|
||||
High = 2
|
||||
}
|
||||
}
|
||||
175
api_sqlsugar/VolPro.Core/BackgroundServices/mail/MailService.cs
Normal file
175
api_sqlsugar/VolPro.Core/BackgroundServices/mail/MailService.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Extensions;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件服务实现
|
||||
/// </summary>
|
||||
public class MailService : IMailService
|
||||
{
|
||||
// 邮件队列Channel
|
||||
private static readonly Channel<MailRequest> _mailQueue;
|
||||
|
||||
// 提供静态访问Channel的方法
|
||||
public static Channel<MailRequest> MailQueue => _mailQueue;
|
||||
|
||||
// 邮件配置信息
|
||||
private static string address { get; set; }
|
||||
private static string authPwd { get; set; }
|
||||
private static string name { get; set; }
|
||||
private static string host { get; set; }
|
||||
private static int port;
|
||||
private static bool enableSsl { get; set; }
|
||||
|
||||
static MailService()
|
||||
{
|
||||
// 初始化邮件队列Channel
|
||||
_mailQueue = Channel.CreateBounded<MailRequest>(new BoundedChannelOptions(1000)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.Wait
|
||||
});
|
||||
|
||||
// 读取邮件配置
|
||||
var section = AppSetting.GetSection("Mail");
|
||||
address = section["Address"];
|
||||
authPwd = section["AuthPwd"];
|
||||
name = section["Name"];
|
||||
host = section["Host"];
|
||||
port = section["Port"].GetInt();
|
||||
enableSsl = section["EnableSsl"].GetBool();
|
||||
}
|
||||
|
||||
public MailService()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实际发送邮件的核心方法
|
||||
/// </summary>
|
||||
private async Task SendEmailCoreAsync(string emails, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
// 解析多个接收人
|
||||
var recipients = new List<string>();
|
||||
if (!string.IsNullOrEmpty(emails))
|
||||
{
|
||||
foreach (var addr in emails.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
recipients.Add(addr.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (recipients.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建邮件消息
|
||||
using var message = new MailMessage
|
||||
{
|
||||
From = new MailAddress(address, name)
|
||||
};
|
||||
|
||||
// 添加收件人
|
||||
foreach (var recipient in recipients)
|
||||
{
|
||||
message.To.Add(recipient);
|
||||
}
|
||||
|
||||
message.Subject = subject;
|
||||
message.Body = body;
|
||||
message.IsBodyHtml = isHtml;
|
||||
|
||||
// 配置SMTP客户端
|
||||
using var smtpClient = new SmtpClient
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
EnableSsl = enableSsl,
|
||||
Credentials = new NetworkCredential(address, authPwd),
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
UseDefaultCredentials = false
|
||||
};
|
||||
|
||||
// 发送邮件
|
||||
await smtpClient.SendMailAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
private async Task SendEmailQueuedAsync(MailRequestMessage mailMessage)
|
||||
{
|
||||
var request = new MailRequest
|
||||
{
|
||||
MailMessage = mailMessage,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
await _mailQueue.Writer.WriteAsync(request);
|
||||
}
|
||||
|
||||
public void Send(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 支持多个接收人,用逗号分隔
|
||||
var mailMessage = new MailRequestMessage
|
||||
{
|
||||
To = email,
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
IsHtml = isHtml
|
||||
};
|
||||
_mailQueue.Writer.TryWrite(new MailRequest
|
||||
{
|
||||
MailMessage = mailMessage,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接发送邮件(同步发送,不加入队列)
|
||||
/// </summary>
|
||||
public async Task SendAsync(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
await SendEmailCoreAsync(email, subject, body, isHtml);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
public async Task SendQueuedAsync(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mailMessage = new MailRequestMessage
|
||||
{
|
||||
To = email,
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
IsHtml = isHtml
|
||||
};
|
||||
|
||||
await SendEmailQueuedAsync(mailMessage);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
api_sqlsugar/VolPro.Core/BaseInterface/IServices.cs
Normal file
10
api_sqlsugar/VolPro.Core/BaseInterface/IServices.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.BaseInterface
|
||||
{
|
||||
public interface IServices
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//using VolPro.Core.Dapper;
|
||||
//using VolPro.Core.DBManager;
|
||||
//using VolPro.Core.EFDbContext;
|
||||
//using VolPro.Core.Extensions;
|
||||
//using VolPro.Entity.DomainModels;
|
||||
//using Microsoft.AspNetCore.Mvc;
|
||||
//using Microsoft.EntityFrameworkCore;
|
||||
//using Newtonsoft.Json;
|
||||
//using System;
|
||||
//using System.Data;
|
||||
//using System.Data.SqlClient;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
|
||||
//namespace VolPro.Core.BaseProvider.DictionaryComponent
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 组件视图,参照:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1
|
||||
// /// 与Controller命名一样必须以ViewComponent结尾
|
||||
// /// </summary>
|
||||
// public class DictionaryViewComponent : ViewComponent
|
||||
// {
|
||||
// public async Task<IViewComponentResult> InvokeAsync(string dropDownIds)
|
||||
// {
|
||||
// if (string.IsNullOrEmpty(dropDownIds))
|
||||
// return null;
|
||||
|
||||
// string[] dicNos = dropDownIds.Split(',');
|
||||
// StringBuilder stringBuilder = new StringBuilder();
|
||||
// SysDbContext context = DBServerProvider.GetEFDbContext();
|
||||
|
||||
|
||||
// var dicData = await context.Queryable<Sys_Dictionary>()
|
||||
// .LeftJoin<Sys_DictionaryList>((o, cus) => o.Dic_ID == cus.Dic_ID)
|
||||
// .Where(d => dicNos.Contains(d.DicNo))
|
||||
// .Select((d, list) => new { list.DicValue, list.DicName, d.Config, d.DbSql, list.OrderNo, d.DicNo })
|
||||
// .ToListAsync();
|
||||
// foreach (var item in dicData.GroupBy(x => x.DicNo))
|
||||
// {
|
||||
// stringBuilder.AppendLine($" var optionConfig{item.Key} = {item.Select(x => x.Config).FirstOrDefault()}");
|
||||
|
||||
// string dbSql = item.Select(s => s.DbSql).FirstOrDefault();
|
||||
|
||||
// stringBuilder.AppendLine($@" var dataSource{item.Key} = {
|
||||
// (!string.IsNullOrEmpty(dbSql)
|
||||
// ? DBServerProvider.GetSqlDapper().QueryList<object>(dbSql, null).Serialize()
|
||||
// : item.OrderByDescending(o => o.OrderNo).
|
||||
// Select(s => new { s.DicName, s.DicValue }).ToList()
|
||||
// .Serialize())
|
||||
// }.convertToValueText(optionConfig{item.Key})");
|
||||
// stringBuilder.AppendLine($" optionConfig{item.Key}.data = dataSource{item.Key};");
|
||||
// }
|
||||
// ViewBag.Dic = stringBuilder.ToString();
|
||||
// return View("~/Views/Shared/Dictionary.cshtml");
|
||||
// }
|
||||
|
||||
// }
|
||||
//}
|
||||
328
api_sqlsugar/VolPro.Core/BaseProvider/IRepository.cs
Normal file
328
api_sqlsugar/VolPro.Core/BaseProvider/IRepository.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Dapper;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.SystemModels;
|
||||
|
||||
namespace VolPro.Core.BaseProvider
|
||||
{
|
||||
public interface IRepository<TEntity> where TEntity : BaseEntity
|
||||
{
|
||||
|
||||
BaseDbContext BaseDbContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// EF DBContext
|
||||
/// </summary>
|
||||
ISqlSugarClient DbContext { get; }
|
||||
|
||||
ISqlSugarClient SqlSugarClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 执行事务。将在执行的方法带入Action
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent DbContextBeginTransaction(Func<WebResponseContent> action);
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="where">查询条件</param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
List<TEntity> Find(Expression<Func<TEntity, bool>> where, bool filterDeleted = true);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="orderBySelector">排序字段,数据格式如:
|
||||
/// orderBy = x => new Dictionary<object, bool>() {
|
||||
/// { x.BalconyName,QueryOrderBy.Asc},
|
||||
/// { x.TranCorpCode1,QueryOrderBy.Desc}
|
||||
/// };
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
TEntity FindFirst(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
|
||||
ISugarQueryable<TEntity> WhereIF([NotNull] Expression<Func<TEntity, object>> field, string value, LinqExpressionType linqExpression = LinqExpressionType.Equal);
|
||||
|
||||
/// <summary>
|
||||
/// if判断查询
|
||||
/// </summary>
|
||||
/// 查询示例,value不为null时参与条件查询
|
||||
/// string value = null;
|
||||
/// repository.WhereIF(value!=null,x=>x.Creator==value);
|
||||
/// <param name="checkCondition"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
ISugarQueryable<TEntity> WhereIF(bool checkCondition, Expression<Func<TEntity, bool>> predicate);
|
||||
|
||||
/// <summary>
|
||||
/// if判断查询
|
||||
/// </summary>
|
||||
/// 查询示例,value不为null时参与条件查询
|
||||
/// string value = null;
|
||||
/// repository.WhereIF<Sys_User>(value!=null,x=>x.Creator==value);
|
||||
/// <param name="checkCondition"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
ISugarQueryable<T> WhereIF<T>(bool checkCondition, Expression<Func<T, bool>> predicate) where T : class, new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate">where条件</param>
|
||||
/// <param name="orderBy">排序字段,数据格式如:
|
||||
/// orderBy = x => new Dictionary<object, bool>() {
|
||||
/// { x.BalconyName,QueryOrderBy.Asc},
|
||||
/// { x.TranCorpCode1,QueryOrderBy.Desc}
|
||||
/// };
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
ISugarQueryable<TEntity> FindAsIQueryable(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, Dictionary<object, QueryOrderBy>>> orderBy = null, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
/// 通过条件查询数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="predicate">查询条件</param>
|
||||
/// <param name="selector">返回类型如:Find(x => x.UserName == loginInfo.userName, p => new { uname = p.UserName });</param>
|
||||
/// <returns></returns>
|
||||
List<T> Find<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据条件,返回查询的类
|
||||
/// </summary>
|
||||
/// <typeparam name="TFind"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
List<TFind> Find<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class,new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TFind"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<TFind> FindAsyncFirst<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class, new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
///<param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<TEntity> FindAsyncFirst(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TFind"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<List<TFind>> FindAsync<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class, new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
///<param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<TEntity> FindFirstAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<List<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
///<param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<List<T>> FindAsync<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<T> FindFirstAsync<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true);
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
bool Exists(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TExists"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
bool Exists<TExists>(Expression<Func<TExists, bool>> predicate, bool filterDeleted = true) where TExists : class, new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TExists"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="filterDeleted">是否过滤逻辑删除的数据,默认过</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ExistsAsync<TExists>(Expression<Func<TExists, bool>> predicate, bool filterDeleted = true) where TExists : class, new();
|
||||
|
||||
ISugarQueryable<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> incluedProperty) where TProperty : new();
|
||||
|
||||
|
||||
|
||||
|
||||
ISugarQueryable<TFind> IQueryablePage<TFind>(int pageIndex, int pagesize, out int rowcount, Expression<Func<TFind, bool>> predicate, Expression<Func<TEntity, Dictionary<object, QueryOrderBy>>> orderBy, bool returnRowCount = true) where TFind : class, new();
|
||||
|
||||
|
||||
ISugarQueryable<TEntity> IQueryablePage(ISugarQueryable<TEntity> queryable, int pageIndex, int pagesize, out int rowcount, Dictionary<string, QueryOrderBy> orderBy, bool returnRowCount = true);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="properties">指定更新字段:x=>new {x.Name,x.Enable}</param>
|
||||
/// <param name="saveChanges">是否保存</param>
|
||||
/// <returns></returns>
|
||||
|
||||
int Update(TEntity entity, Expression<Func<TEntity, object>> properties, bool saveChanges = false);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="properties">指定更新字段:x=>new {x.Name,x.Enable}</param>
|
||||
/// <param name="saveChanges">是否保存</param>
|
||||
/// <returns></returns>
|
||||
int Update<TSource>(TSource entity, Expression<Func<TSource, object>> properties, bool saveChanges = false) where TSource : class, new();
|
||||
|
||||
int Update<TSource>(TSource entity, bool saveChanges = false) where TSource : class, new();
|
||||
|
||||
int Update<TSource>(TSource entity, string[] properties, bool saveChanges = false) where TSource : class, new();
|
||||
|
||||
int UpdateRange<TSource>(IEnumerable<TSource> entities, bool saveChanges = false) where TSource : class, new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="properties">指定更新字段:x=>new {x.Name,x.Enable}</param>
|
||||
/// <param name="saveChanges">是否保存</param>
|
||||
/// <returns></returns>
|
||||
int UpdateRange<TSource>(IEnumerable<TSource> models, Expression<Func<TSource, object>> properties, bool saveChanges = false) where TSource : class, new();
|
||||
|
||||
int UpdateRange<TSource>(IEnumerable<TSource> entities, string[] properties, bool saveChanges = false) where TSource : class, new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
///修改时同时对明细的添加、删除、修改
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="updateDetail">是否修改明细</param>
|
||||
/// <param name="delNotExist">是否删除明细不存在的数据</param>
|
||||
/// <param name="updateMainFields">主表指定修改字段</param>
|
||||
/// <param name="updateDetailFields">明细指定修改字段</param>
|
||||
/// <param name="saveChange">是否保存</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent UpdateRange<Detail>(TEntity entity,
|
||||
bool updateDetail = false,
|
||||
bool delNotExist = false,
|
||||
Expression<Func<TEntity, object>> updateMainFields = null,
|
||||
Expression<Func<Detail, object>> updateDetailFields = null,
|
||||
bool saveChange = false) where Detail : class, new();
|
||||
|
||||
void Delete(TEntity model, bool saveChanges = false);
|
||||
void Delete<T>(T model, bool saveChanges = false) where T : class, new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <param name="delList">是否将子表的数据也删除</param>
|
||||
/// <param name="saveChange">是否执行保存数据库</param>
|
||||
/// <returns></returns>
|
||||
int DeleteWithKeys(object[] keys, bool saveChange = true);
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据并设置自增
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
void AddWithSetIdentity(TEntity entity);
|
||||
|
||||
void AddWithSetIdentity<T>(T entity) where T : class, new();
|
||||
|
||||
void Add(TEntity entities, bool SaveChanges = false);
|
||||
|
||||
void Add<T>(T entities, bool saveChanges = false) where T : class, new();
|
||||
|
||||
// void AddRange(IEnumerable<TEntity> entities, bool SaveChanges = false);
|
||||
|
||||
|
||||
|
||||
void AddRange<T>(List<T> entities, bool saveChanges = false)
|
||||
where T : class, new();
|
||||
|
||||
|
||||
int SaveChanges();
|
||||
|
||||
Task<int> SaveChangesAsync();
|
||||
|
||||
|
||||
|
||||
int ExecuteSqlCommand(string sql, params SugarParameter[] SugarParameters);
|
||||
|
||||
List<TEntity> FromSql(string sql, params SugarParameter[] SugarParameters);
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql
|
||||
/// 使用方式 FormattableString sql=$"select * from xx where name ={xx} and pwd={xx1} ",
|
||||
/// FromSqlInterpolated内部处理sql注入的问题,直接在{xx}写对应的值即可
|
||||
/// 注意:sql必须 select * 返回所有TEntity字段,
|
||||
/// </summary>
|
||||
/// <param name="formattableString"></param>
|
||||
/// <returns></returns>
|
||||
// ISugarQueryable<TEntity> FromSqlInterpolated([System.Diagnostics.CodeAnalysis.NotNull] FormattableString sql);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 取消上下文跟踪(2021.08.22)
|
||||
/// 更新报错时,请调用此方法:The instance of entity type 'XXX' cannot be tracked because another instance with the same key value for {'XX'} is already being tracked.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
void Detached(TEntity entity);
|
||||
void DetachedRange(IEnumerable<TEntity> entities);
|
||||
}
|
||||
}
|
||||
189
api_sqlsugar/VolPro.Core/BaseProvider/IService.cs
Normal file
189
api_sqlsugar/VolPro.Core/BaseProvider/IService.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using VolPro.Core.CacheManager;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Core.WorkFlow;
|
||||
using VolPro.Entity.DomainModels;
|
||||
using VolPro.Entity.SystemModels;
|
||||
|
||||
namespace VolPro.Core.BaseProvider
|
||||
{
|
||||
public interface IService<T> where T : BaseEntity
|
||||
{
|
||||
|
||||
CacheManager.ICacheService CacheContext { get; }
|
||||
Microsoft.AspNetCore.Http.HttpContext Context { get; }
|
||||
string WorkFlowTableName { get; set; }
|
||||
|
||||
ISugarQueryable<T> FindAsIQueryable(Expression<Func<T, bool>> predicate, bool filterDeleted = true);
|
||||
/// <summary>
|
||||
/// 将前端table的查询条件转换为查询ISugarQueryable
|
||||
/// </summary>
|
||||
/// <param name="options">前端查询参数</param>
|
||||
/// <param name="useTenancy">是否使用数据隔离</param>
|
||||
/// <returns></returns>
|
||||
ISugarQueryable<T> FindAsIQueryable(PageDataOptions options, bool useTenancy = true);
|
||||
|
||||
/// <summary>
|
||||
/// 查询
|
||||
/// </summary>
|
||||
/// <param name="pageData"></param>
|
||||
/// <returns></returns>
|
||||
PageGridData<T> GetPageData(PageDataOptions pageData);
|
||||
|
||||
object GetDetailPage(PageDataOptions pageData);
|
||||
|
||||
WebResponseContent Upload(List<IFormFile> files);
|
||||
|
||||
WebResponseContent DownLoadTemplate();
|
||||
|
||||
WebResponseContent Import(List<IFormFile> files);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
/// <param name="pageData"></param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent Export(PageDataOptions pageData);
|
||||
|
||||
/// <summary>
|
||||
/// 新增
|
||||
/// </summary>
|
||||
/// <param name="saveDataModel">主表与子表的数据</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent Add(SaveModel saveDataModel);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="entity">保存的实体</param>
|
||||
/// <param name="validationEntity">是否对实体进行校验</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent AddEntity(T entity, bool validationEntity = true);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TDetail"></typeparam>
|
||||
/// <param name="entity">保存的实体</param>
|
||||
/// <param name="list">保存的明细</param>
|
||||
/// <param name="validationEntity">是否对实体进行校验</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent Add<TDetail>(T entity, List<TDetail> list = null, bool validationEntity = true) where TDetail : class, new();
|
||||
/// <summary>
|
||||
/// 编辑
|
||||
/// </summary>
|
||||
/// <param name="saveDataModel">主表与子表的数据</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent Update(SaveModel saveDataModel);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除数据
|
||||
/// </summary>
|
||||
/// <param name="keys">删除的主键</param>
|
||||
/// <param name="delList">是否删除对应明细(默认会删除明细)</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent Del(object[] keys, bool delList = true);
|
||||
|
||||
WebResponseContent Audit(object[] id, int? auditStatus, string auditReason);
|
||||
|
||||
/// <summary>
|
||||
/// 撤销审批
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <param name="status">1=撤销,-1终止</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent CancelAudit(AntiData antiData, int status);
|
||||
|
||||
/// <summary>
|
||||
/// 催办
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent UrgentAudit(object[] keys);
|
||||
/// <summary>
|
||||
/// 反审
|
||||
/// </summary>
|
||||
/// <param name="antiData"></param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent AntiAudit(AntiData antiData);
|
||||
|
||||
/// <summary>
|
||||
/// 重新生成流程或者回退流程
|
||||
/// </summary>
|
||||
/// <param name="id">表数据id</param>
|
||||
/// <param name="msg">重写流程的日志信息</param>
|
||||
/// <param name="flowWriteState">重新开始或回退到上一级节点</param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent RestartWorkFlowAudit(object id, string msg, FlowWriteState flowWriteState);
|
||||
/// <summary>
|
||||
/// 提交审批数据 2023.11.12
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="flowWriteState"></param>
|
||||
/// <returns></returns>
|
||||
WebResponseContent SubmitWorkFlowAudit(object[] ids);
|
||||
bool AddProcese(T entity);
|
||||
|
||||
(string, T, bool) ApiValidate(string bizContent, Expression<Func<T, object>> expression = null);
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput"></typeparam>
|
||||
/// <param name="bizContent"></param>
|
||||
/// <param name="expression">对指属性验证格式如:x=>new { x.UserName,x.Value }</param>
|
||||
/// <returns>(string,TInput, bool) string:返回验证消息,TInput:bizContent序列化后的对象,bool:验证是否通过</returns>
|
||||
(string, TInput, bool) ApiValidateInput<TInput>(string bizContent, Expression<Func<TInput, object>> expression);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput"></typeparam>
|
||||
/// <param name="bizContent"></param>
|
||||
/// <param name="expression">对指属性验证格式如:x=>new { x.UserName,x.Value }</param>
|
||||
/// <param name="validateExpression">对指定的字段只做合法性判断比如长度是是否超长</param>
|
||||
/// <returns>(string,TInput, bool) string:返回验证消息,TInput:bizContent序列化后的对象,bool:验证是否通过</returns>
|
||||
(string, TInput, bool) ApiValidateInput<TInput>(string bizContent, Expression<Func<TInput, object>> expression, Expression<Func<TInput, object>> validateExpression);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将数据源映射到新的数据中,List<TSource>映射到List<TResult>或TSource映射到TResult
|
||||
/// 目前只支持Dictionary或实体类型
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="resultExpression">只映射返回对象的指定字段</param>
|
||||
/// <param name="sourceExpression">只映射数据源对象的指定字段</param>
|
||||
/// 过滤条件表达式调用方式:List表达式x => new { x[0].MenuName, x[0].Menu_Id},表示指定映射MenuName,Menu_Id字段
|
||||
/// List<Sys_Menu> list = new List<Sys_Menu>();
|
||||
/// list.MapToObject<List<Sys_Menu>, List<Sys_Menu>>(x => new { x[0].MenuName, x[0].Menu_Id}, null);
|
||||
///
|
||||
///过滤条件表达式调用方式:实体表达式x => new { x.MenuName, x.Menu_Id},表示指定映射MenuName,Menu_Id字段
|
||||
/// Sys_Menu sysMenu = new Sys_Menu();
|
||||
/// sysMenu.MapToObject<Sys_Menu, Sys_Menu>(x => new { x.MenuName, x.Menu_Id}, null);
|
||||
/// <returns></returns>
|
||||
TResult MapToEntity<TSource, TResult>(TSource source, Expression<Func<TResult, object>> resultExpression,
|
||||
Expression<Func<TSource, object>> sourceExpression = null) where TResult : class;
|
||||
|
||||
/// <summary>
|
||||
/// 将一个实体的赋到另一个实体上,应用场景:
|
||||
/// 两个实体,a a1= new a();b b1= new b(); a1.P=b1.P; a1.Name=b1.Name;
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="expression">指定对需要的字段赋值,格式x=>new {x.Name,x.P},返回的结果只会对Name与P赋值</param>
|
||||
void MapValueToEntity<TSource, TResult>(TSource source, TResult result, Expression<Func<TResult, object>> expression = null) where TResult : class;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
656
api_sqlsugar/VolPro.Core/BaseProvider/RepositoryBase.cs
Normal file
656
api_sqlsugar/VolPro.Core/BaseProvider/RepositoryBase.cs
Normal file
@@ -0,0 +1,656 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.DirectoryServices;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Dapper;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity;
|
||||
using VolPro.Entity.SystemModels;
|
||||
|
||||
namespace VolPro.Core.BaseProvider
|
||||
{
|
||||
public abstract class RepositoryBase<TEntity> where TEntity : BaseEntity, new()
|
||||
{
|
||||
public RepositoryBase(BaseDbContext dbContext)
|
||||
{
|
||||
this.DefaultDbContext = dbContext;
|
||||
}
|
||||
|
||||
private BaseDbContext DefaultDbContext { get; set; }
|
||||
public BaseDbContext BaseDbContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return DefaultDbContext;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ISqlSugarClient DbContext
|
||||
{
|
||||
get { return DefaultDbContext.SqlSugarClient; }
|
||||
}
|
||||
|
||||
public virtual ISqlSugarClient SqlSugarClient
|
||||
{
|
||||
get
|
||||
{
|
||||
return DefaultDbContext.SqlSugarClient;
|
||||
}
|
||||
}
|
||||
private ISugarQueryable<TEntity> DBSet
|
||||
{
|
||||
get { return BaseDbContext.Set<TEntity>(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行事务
|
||||
/// </summary>
|
||||
/// <param name="action">如果返回false则回滚事务(可自行定义规则)</param>
|
||||
/// <returns></returns>
|
||||
public virtual WebResponseContent DbContextBeginTransaction(Func<WebResponseContent> action)
|
||||
{
|
||||
if (DbContext.Ado.IsAnyTran())
|
||||
{
|
||||
return action();
|
||||
}
|
||||
WebResponseContent webResponse = new WebResponseContent();
|
||||
try
|
||||
{
|
||||
DbContext.Ado.BeginTran();
|
||||
|
||||
webResponse = action();
|
||||
if (webResponse.Status)
|
||||
{
|
||||
DbContext.Ado.CommitTran();
|
||||
}
|
||||
else
|
||||
{
|
||||
DbContext.Ado.RollbackTran();
|
||||
|
||||
}
|
||||
return webResponse;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DbContext.Ado.RollbackTran();
|
||||
string message = ex.Message + ex?.InnerException + ex?.StackTrace;
|
||||
if (HttpContext.Current.GetService<Microsoft.AspNetCore.Hosting.IWebHostEnvironment>().IsDevelopment())
|
||||
{
|
||||
return webResponse.Error(message);
|
||||
}
|
||||
Logger.Error(message);
|
||||
return webResponse.Error("处理异常");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Exists<TExists>(Expression<Func<TExists, bool>> predicate, bool filterDeleted = true) where TExists : class, new()
|
||||
{
|
||||
return BaseDbContext.Set<TExists>(filterDeleted).Any(predicate);
|
||||
}
|
||||
|
||||
public virtual Task<bool> ExistsAsync<TExists>(Expression<Func<TExists, bool>> predicate, bool filterDeleted = true) where TExists : class, new()
|
||||
{
|
||||
return BaseDbContext.Set<TExists>(filterDeleted).AnyAsync(predicate);
|
||||
}
|
||||
|
||||
public virtual bool Exists(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
var query = BaseDbContext.Set<TEntity>(filterDeleted);
|
||||
if (typeof(TEntity).GetSugarSplitTable() != null)
|
||||
{
|
||||
return query.SplitTable().Any(predicate);
|
||||
}
|
||||
return query.Any(predicate);
|
||||
}
|
||||
|
||||
public virtual Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return BaseDbContext.Set<TEntity>(filterDeleted).AnyAsync(predicate);
|
||||
}
|
||||
/// <summary>
|
||||
/// 查询字段不为null或者为空
|
||||
/// </summary>
|
||||
/// <param name="field">x=>new {x.字段}</param>
|
||||
/// <param name="value">查询的类</param>
|
||||
/// <param name="linqExpression">查询类型</param>
|
||||
/// <returns></returns>
|
||||
public virtual ISugarQueryable<TEntity> WhereIF([NotNull] Expression<Func<TEntity, object>> field, string value, LinqExpressionType linqExpression = LinqExpressionType.Equal)
|
||||
{
|
||||
return BaseDbContext.Set<TEntity>().WhereNotEmpty(field, value, linqExpression);
|
||||
}
|
||||
|
||||
public virtual ISugarQueryable<TEntity> WhereIF(bool checkCondition, Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
if (checkCondition)
|
||||
{
|
||||
return BaseDbContext.Set<TEntity>().Where(predicate);
|
||||
}
|
||||
return BaseDbContext.Set<TEntity>();
|
||||
}
|
||||
|
||||
public virtual ISugarQueryable<T> WhereIF<T>(bool checkCondition, Expression<Func<T, bool>> predicate) where T : class, new()
|
||||
{
|
||||
if (checkCondition)
|
||||
{
|
||||
return BaseDbContext.Set<T>().Where(predicate);
|
||||
}
|
||||
return BaseDbContext.Set<T>();
|
||||
}
|
||||
|
||||
|
||||
public virtual List<TFind> Find<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class, new()
|
||||
{
|
||||
return BaseDbContext.Set<TFind>(filterDeleted).Where(predicate).ToList();
|
||||
}
|
||||
|
||||
public virtual async Task<TFind> FindAsyncFirst<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class, new()
|
||||
{
|
||||
return await FindAsISugarQueryable(predicate, filterDeleted).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<TEntity> FindAsyncFirst(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return await FindAsISugarQueryable<TEntity>(predicate, filterDeleted).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<List<TFind>> FindAsync<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class, new()
|
||||
{
|
||||
return await FindAsISugarQueryable<TFind>(predicate, filterDeleted).ToListAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<List<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return await FindAsISugarQueryable(predicate, filterDeleted).ToListAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<TEntity> FindFirstAsync(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return await FindAsISugarQueryable(predicate, filterDeleted).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<List<T>> FindAsync<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true)
|
||||
{
|
||||
return await FindAsISugarQueryable(predicate, filterDeleted).Select(selector).ToListAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<T> FindFirstAsync<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true)
|
||||
{
|
||||
return await FindAsISugarQueryable(predicate, filterDeleted).Select(selector).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
private ISugarQueryable<TFind> FindAsISugarQueryable<TFind>(Expression<Func<TFind, bool>> predicate, bool filterDeleted = true) where TFind : class,new()
|
||||
{
|
||||
return BaseDbContext.Set<TFind>(filterDeleted).Where(predicate);
|
||||
}
|
||||
|
||||
|
||||
public virtual List<T> Find<T>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> selector, bool filterDeleted = true)
|
||||
{
|
||||
return BaseDbContext.Set<TEntity>(filterDeleted).Where(predicate).Select(selector).ToList();
|
||||
}
|
||||
/// <summary>
|
||||
/// 单表查询
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public virtual List<TEntity> Find(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return FindAsISugarQueryable(predicate, filterDeleted).ToList();
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name=""></param>
|
||||
/// <param name="orderBy">排序字段</param>
|
||||
/// <returns></returns>
|
||||
public virtual TEntity FindFirst(Expression<Func<TEntity, bool>> predicate, bool filterDeleted = true)
|
||||
{
|
||||
return BaseDbContext.Set<TEntity>(filterDeleted).Where(predicate).FirstOrDefault();
|
||||
}
|
||||
|
||||
|
||||
public ISugarQueryable<TEntity> FindAsIQueryable(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, Dictionary<object, QueryOrderBy>>> orderBy = null, bool filterDeleted = true)
|
||||
{
|
||||
//if (orderBy != null)
|
||||
// return DbContext.Set<TEntity>().Where(predicate).GetISugarQueryableOrderBy(orderBy.GetExpressionToDic());
|
||||
return DbContext.Set<TEntity>(filterDeleted).Where(predicate);
|
||||
}
|
||||
|
||||
public ISugarQueryable<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> incluedProperty) where TProperty : new()
|
||||
{
|
||||
return DbContext.Set<TEntity>().Include(incluedProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过条件查询返回指定列的数据(将TEntity映射到匿名或实体T)
|
||||
///var result = Sys_UserRepository.GetInstance.Find(x => x.UserName == loginInfo.userName, p => new { uname = p.UserName });
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pagesize"></param>
|
||||
/// <param name="rowcount"></param>
|
||||
/// <param name="predicate">查询条件</param>
|
||||
/// <param name="orderBySelector">多个排序字段key为字段,value为升序/降序</param>
|
||||
/// <returns></returns>
|
||||
public virtual ISugarQueryable<TFind> IQueryablePage<TFind>(int pageIndex, int pagesize, out int rowcount, Expression<Func<TFind, bool>> predicate, Expression<Func<TEntity, Dictionary<object, QueryOrderBy>>> orderBy, bool returnRowCount = true) where TFind : class, new()
|
||||
{
|
||||
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
|
||||
pagesize = pagesize <= 0 ? 10 : pagesize;
|
||||
if (predicate == null)
|
||||
{
|
||||
predicate = x => 1 == 1;
|
||||
}
|
||||
var _db = DbContext.Set<TFind>();
|
||||
rowcount = returnRowCount ? _db.Count(predicate) : 0;
|
||||
return DbContext.Set<TFind>().Where(predicate)
|
||||
.GetISugarQueryableOrderBy(orderBy.GetExpressionToDic())
|
||||
.Skip((pageIndex - 1) * pagesize)
|
||||
.Take(pagesize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页排序
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pagesize"></param>
|
||||
/// <param name="rowcount"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <returns></returns>
|
||||
public virtual ISugarQueryable<TEntity> IQueryablePage(ISugarQueryable<TEntity> queryable, int pageIndex, int pagesize, out int rowcount, Dictionary<string, QueryOrderBy> orderBy, bool returnRowCount = true)
|
||||
{
|
||||
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
|
||||
pagesize = pagesize <= 0 ? 10 : pagesize;
|
||||
rowcount = returnRowCount ? queryable.Count() : 0;
|
||||
return queryable.GetISugarQueryableOrderBy<TEntity>(orderBy)
|
||||
.Skip((pageIndex - 1) * pagesize)
|
||||
.Take(pagesize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新表数据
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="saveChanges">是否保存</param>
|
||||
/// <param name="properties">格式 Expression<Func<entityt, object>> expTree = x => new { x.字段1, x.字段2 };</param>
|
||||
public virtual int Update(TEntity entity, Expression<Func<TEntity, object>> properties, bool saveChanges = false)
|
||||
{
|
||||
return Update<TEntity>(entity, properties, saveChanges);
|
||||
}
|
||||
|
||||
public virtual int Update<TSource>(TSource entity, Expression<Func<TSource, object>> properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange(new List<TSource>
|
||||
{
|
||||
entity
|
||||
}, properties, saveChanges);
|
||||
}
|
||||
|
||||
|
||||
public virtual int Update<TSource>(TSource entity, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(new List<TSource>() { entity }, properties, saveChanges);
|
||||
}
|
||||
public virtual int Update<TSource>(TSource entity, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(new List<TSource>() { entity }, new string[0], saveChanges);
|
||||
}
|
||||
public virtual int UpdateRange<TSource>(IEnumerable<TSource> entities, Expression<Func<TSource, object>> properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(entities, properties?.GetExpressionProperty(), saveChanges);
|
||||
}
|
||||
public virtual int UpdateRange<TSource>(IEnumerable<TSource> entities, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(entities, new string[0], saveChanges);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新表数据
|
||||
/// </summary>
|
||||
/// <param name="models"></param>
|
||||
/// <param name="properties">格式 Expression<Func<entityt, object>> expTree = x => new { x.字段1, x.字段2 };</param>
|
||||
public int UpdateRange<TSource>(IEnumerable<TSource> entities, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return DbContext.UpdateRange(entities, properties, saveChanges);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="updateDetail">是否修改明细</param>
|
||||
/// <param name="delNotExist">是否删除明细不存在的数据</param>
|
||||
/// <param name="updateMainFields">主表指定修改字段</param>
|
||||
/// <param name="updateDetailFields">明细指定修改字段</param>
|
||||
/// <param name="saveChange">是否保存</param>
|
||||
/// <returns></returns>
|
||||
public virtual WebResponseContent UpdateRange<Detail>(TEntity entity,
|
||||
bool updateDetail = false,
|
||||
bool delNotExist = false,
|
||||
Expression<Func<TEntity, object>> updateMainFields = null,
|
||||
Expression<Func<Detail, object>> updateDetailFields = null,
|
||||
bool saveChange = false) where Detail : class, new()
|
||||
{
|
||||
WebResponseContent webResponse = new WebResponseContent();
|
||||
Update(entity, updateMainFields);
|
||||
string message = "";
|
||||
if (updateDetail)
|
||||
{
|
||||
PropertyInfo[] properties = typeof(TEntity).GetProperties();
|
||||
PropertyInfo detail = properties.Where(x => x.PropertyType.Name == "List`1").ToList().FirstOrDefault();
|
||||
if (detail != null)
|
||||
{
|
||||
PropertyInfo key = properties.GetKeyProperty();
|
||||
object obj = detail.GetValue(entity);
|
||||
Type detailType = typeof(TEntity).GetCustomAttribute<EntityAttribute>().DetailTable[0];
|
||||
var list = obj as List<Detail>;
|
||||
if (list.Count > 0)
|
||||
{
|
||||
message = UpdateDetail<Detail>(list, key.Name, key.GetValue(entity), updateDetailFields, delNotExist);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!saveChange) return webResponse.OK();
|
||||
|
||||
DbContext.SaveChanges();
|
||||
return webResponse.OK("修改成功,明细" + message, entity);
|
||||
}
|
||||
private string UpdateDetail<TDetail>(List<TDetail> list,
|
||||
string keyName,
|
||||
object keyValue,
|
||||
Expression<Func<TDetail, object>> updateDetailFields = null,
|
||||
bool delNotExist = false) where TDetail : class, new()
|
||||
{
|
||||
if (list == null) return "";
|
||||
PropertyInfo property = typeof(TDetail).GetKeyProperty();
|
||||
string detailKeyName = property.Name;
|
||||
var details = DbContext.Set<TDetail>();
|
||||
Expression<Func<TDetail, object>> selectExpression = detailKeyName.GetExpression<TDetail, object>();
|
||||
Expression<Func<TDetail, bool>> whereExpression = keyName.CreateExpression<TDetail>(keyValue, LinqExpressionType.Equal);
|
||||
//这里有问题, Expression<Func<TDetail, object>>会转换为查询所有字段20231020
|
||||
//List<object> detailKeys = details.Where(whereExpression).Select(selectExpression).ToList();
|
||||
|
||||
List<object> detailKeys = details.Where(whereExpression).ToList().Select(selectExpression.Compile()).ToList();
|
||||
//获取主键默认值
|
||||
//string keyDefaultVal = property.PropertyType==typeof(string)?"": property.PropertyType.Assembly.CreateInstance(property.PropertyType.FullName).ToString();
|
||||
string keyDefaultVal = "";
|
||||
if (property.PropertyType != typeof(string))
|
||||
keyDefaultVal = property.PropertyType.Assembly.CreateInstance(property.PropertyType.FullName).ToString();
|
||||
int addCount = 0;
|
||||
int editCount = 0;
|
||||
int delCount = 0;
|
||||
PropertyInfo mainKeyProperty = typeof(TDetail).GetProperty(keyName);
|
||||
|
||||
var detailKeyPro = typeof(TDetail).GetKeyProperty();
|
||||
IdWorker worker = null;
|
||||
bool stringKey = false;
|
||||
if (detailKeyPro.PropertyType == typeof(string))
|
||||
{
|
||||
stringKey = true;
|
||||
if (AppSetting.UseSnow)
|
||||
{
|
||||
worker = new IdWorker();
|
||||
}
|
||||
}
|
||||
List<TDetail> addList = new List<TDetail>();
|
||||
List<TDetail> updateList = new List<TDetail>();
|
||||
List<object> keys = new List<object>();
|
||||
list.ForEach(x =>
|
||||
{
|
||||
object val = property.GetValue(x) ?? "";
|
||||
//主键是默认值的为新增的数据
|
||||
if (val.ToString() == keyDefaultVal)
|
||||
{
|
||||
x.SetCreateDefaultVal();
|
||||
//设置主表的值,也可以不设置
|
||||
mainKeyProperty.SetValue(x, keyValue);
|
||||
if (stringKey)
|
||||
{
|
||||
if (worker != null)
|
||||
{
|
||||
detailKeyPro.SetValue(x, worker.NextId().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
detailKeyPro.SetValue(x, Guid.NewGuid().ToString());
|
||||
}
|
||||
}
|
||||
// DbContext.Insertable(x).AddQueue();
|
||||
addList.Add(x);
|
||||
addCount++;
|
||||
}
|
||||
else//修改的数据
|
||||
{
|
||||
//获取所有修改的key,如果从数据库查来的key,不在修改中的key,则为删除的数据
|
||||
keys.Add(val);
|
||||
x.SetModifyDefaultVal();
|
||||
// Update<TDetail>(x, updateDetailFields);
|
||||
updateList.Add(x);
|
||||
// repository.DbContext.Entry<TDetail>(x).State = EntityState.Modified;
|
||||
editCount++;
|
||||
}
|
||||
});
|
||||
//删除
|
||||
if (delNotExist)
|
||||
{
|
||||
detailKeys.Where(x => !keys.Contains(x)).ToList().ForEach(d =>
|
||||
{
|
||||
delCount++;
|
||||
TDetail detail = Activator.CreateInstance<TDetail>();
|
||||
property.SetValue(detail, d);
|
||||
DbContext.Deleteable<TDetail>(detail).AddQueue();
|
||||
for (int i = 0; i < list.Count(); i++)
|
||||
{
|
||||
if (property.GetValue(list[i]) == d)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
DbContext.Insertable<TDetail>(addList).ExecuteCommand();
|
||||
if (updateDetailFields == null)
|
||||
{
|
||||
DbContext.Updateable<TDetail>(updateList).AddQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
DbContext.Updateable<TDetail>(updateList).UpdateColumns(updateDetailFields.GetExpressionToArray<TDetail>()).ExecuteCommand();
|
||||
}
|
||||
return $"修改[{editCount}]条,新增[{addCount}]条,删除[{delCount}]条";
|
||||
}
|
||||
|
||||
public virtual void Delete(TEntity model, bool saveChanges = false)
|
||||
{
|
||||
if (typeof(TEntity).GetSugarSplitTable() != null)
|
||||
{
|
||||
DbContext.Deleteable(model).SplitTable().ExecuteCommand();
|
||||
return;
|
||||
}
|
||||
DbContext.Deleteable(model).AddQueue();
|
||||
if (saveChanges)
|
||||
{
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Delete<T>(T model, bool saveChanges) where T : class, new()
|
||||
{
|
||||
if (typeof(T).GetSugarSplitTable() != null)
|
||||
{
|
||||
DbContext.Deleteable(model).SplitTable().ExecuteCommand();
|
||||
return;
|
||||
}
|
||||
DbContext.Deleteable(model).AddQueue();
|
||||
if (saveChanges)
|
||||
{
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 通过主键批量删除
|
||||
/// </summary>
|
||||
/// <param name="keys">主键key</param>
|
||||
/// <param name="delList">是否连明细一起删除</param>
|
||||
/// <returns></returns>
|
||||
public virtual int DeleteWithKeys(object[] keys, bool saveChange = false)
|
||||
{
|
||||
var keyPro = typeof(TEntity).GetKeyProperty();
|
||||
List<TEntity> list = new List<TEntity>();
|
||||
foreach (var key in keys.Distinct())
|
||||
{
|
||||
TEntity entity = Activator.CreateInstance<TEntity>();
|
||||
keyPro.SetValue(entity, key.ChangeType(keyPro.PropertyType));
|
||||
list.Add(entity);
|
||||
}
|
||||
if (typeof(TEntity).GetSugarSplitTable() != null)
|
||||
{
|
||||
DbContext.Deleteable(list).SplitTable().ExecuteCommand();
|
||||
return keys.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
DbContext.Deleteable(list).AddQueue();
|
||||
}
|
||||
if (saveChange)
|
||||
{
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
return keys.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入数据并设置自增
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public virtual void AddWithSetIdentity(TEntity entity)
|
||||
{
|
||||
AddWithSetIdentity<TEntity>(entity);
|
||||
}
|
||||
public virtual void AddWithSetIdentity<T>(T entity) where T : class, new()
|
||||
{
|
||||
if (typeof(T).GetSugarSplitTable() != null)
|
||||
{
|
||||
DbContext.Insertable(entity).SplitTable().ExecuteCommand();
|
||||
return;
|
||||
}
|
||||
DbContext.Insertable(entity).ExecuteReturnEntity();
|
||||
}
|
||||
public virtual void Add(TEntity entities, bool saveChanges = false)
|
||||
{
|
||||
AddRange(new List<TEntity>() { entities }, saveChanges);
|
||||
}
|
||||
|
||||
public virtual void Add<T>(T entities, bool saveChanges = false) where T : class, new()
|
||||
{
|
||||
DbContext.Insertable(entities).AddQueue();
|
||||
if (saveChanges) DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual void AddRange(List<TEntity> entities, bool saveChanges = false)
|
||||
{
|
||||
AddRange<TEntity>(entities, saveChanges);
|
||||
}
|
||||
|
||||
public virtual void AddRange<T>(List<T> entities, bool saveChanges = false) where T : class, new()
|
||||
{
|
||||
if (AppSetting.UseSnow)
|
||||
{
|
||||
PropertyInfo keyPro = typeof(T).GetKeyProperty();
|
||||
if (keyPro.PropertyType == typeof(long))
|
||||
{
|
||||
//生成雪花id
|
||||
var idWorker = new IdWorker();
|
||||
foreach (var item in entities)
|
||||
{
|
||||
keyPro.SetValue(item, idWorker.NextId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof(T).GetSugarSplitTable() != null)
|
||||
{
|
||||
DbContext.Insertable(entities).SplitTable().ExecuteCommand();
|
||||
return;
|
||||
}
|
||||
DbContext.Insertable(entities).AddQueue();
|
||||
if (saveChanges) DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual int SaveChanges()
|
||||
{
|
||||
return BaseDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual Task<int> SaveChangesAsync()
|
||||
{
|
||||
return BaseDbContext.SqlSugarClient.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public virtual int ExecuteSqlCommand(string sql, params SugarParameter[] SugarParameters)
|
||||
{
|
||||
return DbContext.Ado.ExecuteCommand(sql, SugarParameters);
|
||||
// return DbContext.Database.ExecuteSqlRaw(sql, SugarParameters);
|
||||
}
|
||||
|
||||
public virtual List<TEntity> FromSql(string sql, params SugarParameter[] SugarParameters)
|
||||
{
|
||||
return DbContext.Ado.SqlQuery<TEntity>(sql, SugarParameters).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql
|
||||
/// 使用方式 FormattableString sql=$"select * from xx where name ={xx} and pwd={xx1} ",
|
||||
/// FromSqlInterpolated内部处理sql注入的问题,直接在{xx}写对应的值即可
|
||||
/// 注意:sql必须 select * 返回所有TEntity字段,
|
||||
/// </summary>
|
||||
/// <param name="formattableString"></param>
|
||||
/// <returns></returns>
|
||||
//public virtual ISugarQueryable<TEntity> FromSqlInterpolated([NotNull] FormattableString sql)
|
||||
//{
|
||||
// //DBSet.FromSqlInterpolated(sql).Select(x => new { x,xxx}).ToList();
|
||||
// return DbContext.Ado.SqlQuery<TEntity>(sql);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 取消上下文跟踪
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public virtual void Detached(TEntity entity)
|
||||
{
|
||||
// DbContext.Entry(entity).State = EntityState.Detached;
|
||||
}
|
||||
public virtual void DetachedRange(IEnumerable<TEntity> entities)
|
||||
{
|
||||
//foreach (var entity in entities)
|
||||
//{
|
||||
// DbContext.Entry(entity).State = EntityState.Detached;
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.IO;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
|
||||
namespace VolPro.Core.BaseProvider.ServerMapPath
|
||||
{
|
||||
public interface IPathProvider : IDependency
|
||||
{
|
||||
string MapPath(string path);
|
||||
string MapPath(string path, bool rootPath);
|
||||
IWebHostEnvironment GetHostingEnvironment();
|
||||
}
|
||||
|
||||
public class PathProvider : IPathProvider
|
||||
{
|
||||
private IWebHostEnvironment _hostingEnvironment;
|
||||
|
||||
public PathProvider(IWebHostEnvironment environment)
|
||||
{
|
||||
_hostingEnvironment = environment;
|
||||
}
|
||||
public IWebHostEnvironment GetHostingEnvironment()
|
||||
{
|
||||
return _hostingEnvironment;
|
||||
}
|
||||
|
||||
public string MapPath(string path)
|
||||
{
|
||||
return MapPath(path, false);
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="rootPath">获取wwwroot路径</param>
|
||||
/// <returns></returns>
|
||||
public string MapPath(string path, bool rootPath)
|
||||
{
|
||||
if (rootPath)
|
||||
{
|
||||
if (_hostingEnvironment.WebRootPath == null)
|
||||
{
|
||||
_hostingEnvironment.WebRootPath = _hostingEnvironment.ContentRootPath + "/wwwroot".ReplacePath();
|
||||
}
|
||||
return Path.Combine(_hostingEnvironment.WebRootPath, path).ReplacePath();
|
||||
}
|
||||
return Path.Combine(_hostingEnvironment.ContentRootPath, path).ReplacePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
2777
api_sqlsugar/VolPro.Core/BaseProvider/ServiceBase.cs
Normal file
2777
api_sqlsugar/VolPro.Core/BaseProvider/ServiceBase.cs
Normal file
File diff suppressed because it is too large
Load Diff
132
api_sqlsugar/VolPro.Core/CacheManager/DbCache.cs
Normal file
132
api_sqlsugar/VolPro.Core/CacheManager/DbCache.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.CacheManager
|
||||
{
|
||||
public static class DbCache
|
||||
{
|
||||
private static List<Sys_DbService> DbServices = null;
|
||||
private static object _lock_sbcnew = new object();
|
||||
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
DbServices = DbManger.SysDbContext.Set<Sys_DbService>().Select(s => new
|
||||
{
|
||||
s.Pwd,
|
||||
s.DbIpAddress,
|
||||
s.DatabaseName,
|
||||
s.DbServiceId,
|
||||
s.GroupId,
|
||||
s.UserId,
|
||||
s.Remark,
|
||||
s.PhoneNo,
|
||||
s.DbServiceName
|
||||
})
|
||||
//.Where(x => x.DatabaseName != null && x.DbIpAddress != null && x.UserId != null && x.Pwd != null)
|
||||
.ToList()
|
||||
.Select(s => new Sys_DbService()
|
||||
{
|
||||
Pwd = s.Pwd,
|
||||
DbIpAddress = s.DbIpAddress,
|
||||
DatabaseName = s.DatabaseName,
|
||||
DbServiceName = s.DbServiceName,
|
||||
DbServiceId = s.DbServiceId,
|
||||
GroupId= s.GroupId,
|
||||
UserId = s.UserId,
|
||||
Remark = s.Remark,
|
||||
PhoneNo = s.PhoneNo
|
||||
}).ToList();
|
||||
InitConnection();
|
||||
|
||||
|
||||
}
|
||||
public static List<Sys_DbService> GetList()
|
||||
{
|
||||
return DbServices;
|
||||
}
|
||||
|
||||
public static WebResponseContent Reload(WebResponseContent webResponse)
|
||||
{
|
||||
if (webResponse.Status)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
return webResponse;
|
||||
}
|
||||
|
||||
public static void InitConnection()
|
||||
{
|
||||
foreach (var item in DbServices)
|
||||
{
|
||||
InitConnection(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InitConnection(Sys_DbService item, string databaseName = null)
|
||||
{
|
||||
string connectionString = GetConnectionString(item, databaseName);
|
||||
|
||||
if (databaseName == null)
|
||||
{
|
||||
DBServerProvider.SetConnection(item.DbServiceId.ToString(), connectionString);
|
||||
}
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
public static string GetConnectionString(Sys_DbService item, string databaseName = null)
|
||||
{
|
||||
string connectionString = null;
|
||||
switch (DBType.Name)
|
||||
{
|
||||
//mysql如果端口不是3306,这里也需要修改
|
||||
case "MySql":
|
||||
connectionString = @$" Data Source={item.DbIpAddress};Database={databaseName ?? item.DatabaseName};AllowLoadLocalInfile=true;User ID={item.UserId};Password={item.Pwd};allowPublicKeyRetrieval=true;pooling=true;CharSet=utf8;port=3306;sslmode=none;";
|
||||
break;
|
||||
case "PgSql":
|
||||
connectionString = $"Host={item.DbIpAddress};Port=5432;User id={item.UserId};password={item.Pwd};Database={databaseName ?? item.DatabaseName};";
|
||||
|
||||
break;
|
||||
case "MsSql":
|
||||
connectionString = @$"Data Source={item.DbIpAddress};Initial Catalog={databaseName ?? item.DatabaseName};Persist Security Info=True;User ID={item.UserId};Password={item.Pwd};Connect Timeout=500;Max Pool Size = 512;TrustServerCertificate=True;";
|
||||
|
||||
break;
|
||||
case "DM":
|
||||
// 老版本 :PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA
|
||||
//新版本: Server=localhost; User Id=SYSDBA; PWD=SYSDBA;DATABASE=新DB
|
||||
connectionString = $" Server={item.DbIpAddress}; User Id={item.UserId}; PWD={item.Pwd};DATABASE={databaseName ?? item.DatabaseName}";
|
||||
break;
|
||||
case "Oracle":
|
||||
Console.WriteLine($"未实现数据库:{DBType.Name}");
|
||||
break;
|
||||
}
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
|
||||
public static Sys_DbService GetDbInfo(Guid dbServiceId)
|
||||
{
|
||||
return DbServices.Where(x => x.DbServiceId == dbServiceId).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<Sys_DbService> GetDbInfo(Func<Sys_DbService, bool> where)
|
||||
{
|
||||
return DbServices.Where(where);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.CacheManager
|
||||
{
|
||||
public interface ICacheService : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证缓存项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
bool Exists(string key);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// List写入head
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="val"></param>
|
||||
void LPush(string key, string val);
|
||||
|
||||
void RPush(string key, string val);
|
||||
/// <summary>
|
||||
/// List出队 lpop
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
object ListDequeue(string key);
|
||||
|
||||
/// <summary>
|
||||
/// List出队 lpop
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
T ListDequeue<T>(string key) where T : class;
|
||||
|
||||
/// <summary>
|
||||
/// 移除list中的数据,keepIndex为保留的位置到最后一个元素如list 元素为1.2.3.....100
|
||||
/// 需要移除前3个数,keepindex应该为4
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="keepIndex"></param>
|
||||
void ListRemove(string key, int keepIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 添加缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <param name="value">缓存Value</param>
|
||||
/// <param name="expiresIn">缓存时长</param>
|
||||
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间) //new TimeSpan(0, 60, 0);</param>
|
||||
/// <returns></returns>
|
||||
bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false);
|
||||
|
||||
bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false);
|
||||
|
||||
/// <summary>
|
||||
/// 删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
bool Remove(string key);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key集合</param>
|
||||
/// <returns></returns>
|
||||
void RemoveAll(IEnumerable<string> keys);
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
T Get<T>(string key) where T : class;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
string Get(string key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace VolPro.Core.CacheManager
|
||||
{
|
||||
public class MemoryCacheService : ICacheService
|
||||
{
|
||||
protected IMemoryCache _cache;
|
||||
public MemoryCacheService(IMemoryCache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 验证缓存项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public bool Exists(string key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
return _cache.Get(key) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <param name="value">缓存Value</param>
|
||||
/// <returns></returns>
|
||||
public bool Add(string key, object value)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
_cache.Set(key, value);
|
||||
return Exists(key);
|
||||
}
|
||||
|
||||
public bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false)
|
||||
{
|
||||
if (expireSeconds != -1)
|
||||
{
|
||||
_cache.Set(key,
|
||||
value,
|
||||
new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(new TimeSpan(0, 0, expireSeconds))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.Set(key, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
|
||||
{
|
||||
return AddObject(key, value, expireSeconds, isSliding);
|
||||
}
|
||||
public void LPush(string key, string val)
|
||||
{
|
||||
}
|
||||
public void RPush(string key, string val)
|
||||
{
|
||||
}
|
||||
public T ListDequeue<T>(string key) where T : class
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public object ListDequeue(string key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public void ListRemove(string key, int keepIndex)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <param name="value">缓存Value</param>
|
||||
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
|
||||
/// <param name="expiressAbsoulte">绝对过期时长</param>
|
||||
/// <returns></returns>
|
||||
public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
|
||||
{
|
||||
_cache.Set(key, value,
|
||||
new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(expiresSliding)
|
||||
.SetAbsoluteExpiration(expiressAbsoulte)
|
||||
);
|
||||
|
||||
return Exists(key);
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <param name="value">缓存Value</param>
|
||||
/// <param name="expiresIn">缓存时长</param>
|
||||
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
|
||||
/// <returns></returns>
|
||||
public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
|
||||
{
|
||||
if (isSliding)
|
||||
_cache.Set(key, value,
|
||||
new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(expiresIn)
|
||||
);
|
||||
else
|
||||
_cache.Set(key, value,
|
||||
new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(expiresIn)
|
||||
);
|
||||
|
||||
return Exists(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(string key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
_cache.Remove(key);
|
||||
|
||||
return !Exists(key);
|
||||
}
|
||||
/// <summary>
|
||||
/// 批量删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key集合</param>
|
||||
/// <returns></returns>
|
||||
public void RemoveAll(IEnumerable<string> keys)
|
||||
{
|
||||
if (keys == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keys));
|
||||
}
|
||||
|
||||
keys.ToList().ForEach(item => _cache.Remove(item));
|
||||
}
|
||||
public string Get(string key)
|
||||
{
|
||||
return _cache.Get(key)?.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
return _cache.Get(key) as T;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_cache != null)
|
||||
_cache.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using CSRedis;
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
|
||||
namespace VolPro.Core.CacheManager
|
||||
{
|
||||
public class RedisCacheService : ICacheService
|
||||
{
|
||||
public RedisCacheService()
|
||||
{
|
||||
var csredis = new CSRedisClient(AppSetting.RedisConnectionString);
|
||||
RedisHelper.Initialization(csredis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证缓存项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public bool Exists(string key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
return RedisHelper.Exists(key);
|
||||
}
|
||||
|
||||
public void LPush(string key, string val)
|
||||
{
|
||||
RedisHelper.LPush(key, val);
|
||||
}
|
||||
|
||||
public void RPush(string key, string val)
|
||||
{
|
||||
RedisHelper.RPush(key, val);
|
||||
}
|
||||
|
||||
public T ListDequeue<T>(string key) where T : class
|
||||
{
|
||||
string value = RedisHelper.RPop(key);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
public object ListDequeue(string key)
|
||||
{
|
||||
string value = RedisHelper.RPop(key);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除list中的数据,keepIndex为保留的位置到最后一个元素如list 元素为1.2.3.....100
|
||||
/// 需要移除前3个数,keepindex应该为4
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="keepIndex"></param>
|
||||
public void ListRemove(string key, int keepIndex)
|
||||
{
|
||||
RedisHelper.LTrim(key, keepIndex, -1);
|
||||
}
|
||||
public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
|
||||
{
|
||||
return RedisHelper.Set(key, value, expireSeconds);
|
||||
}
|
||||
public bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false)
|
||||
{
|
||||
return RedisHelper.Set(key, value, expireSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(string key)
|
||||
{
|
||||
RedisHelper.Del(key);
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 批量删除缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key集合</param>
|
||||
/// <returns></returns>
|
||||
public void RemoveAll(IEnumerable<string> keys)
|
||||
{
|
||||
RedisHelper.Del(keys.ToArray());
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
return RedisHelper.Get<T>(key);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存Key</param>
|
||||
/// <returns></returns>
|
||||
public string Get(string key)
|
||||
{
|
||||
return RedisHelper.Get(key);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
236
api_sqlsugar/VolPro.Core/Configuration/AppSetting.cs
Normal file
236
api_sqlsugar/VolPro.Core/Configuration/AppSetting.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Extensions;
|
||||
|
||||
namespace VolPro.Core.Configuration
|
||||
{
|
||||
public static class AppSetting
|
||||
{
|
||||
public static IConfiguration Configuration { get; private set; }
|
||||
|
||||
public static string DbConnectionString
|
||||
{
|
||||
get { return _connection.DbConnectionString; }
|
||||
}
|
||||
|
||||
public static string RedisConnectionString
|
||||
{
|
||||
get { return _connection.RedisConnectionString; }
|
||||
}
|
||||
|
||||
public static bool UseRedis
|
||||
{
|
||||
get { return _connection.UseRedis; }
|
||||
}
|
||||
public static bool UseSignalR
|
||||
{
|
||||
get { return _connection.UseSignalR; }
|
||||
}
|
||||
|
||||
public static bool UseSqlserver2008
|
||||
{
|
||||
get { return _connection.UseSqlserver2008; }
|
||||
}
|
||||
public static Secret Secret { get; private set; }
|
||||
|
||||
public static CreateMember CreateMember { get; private set; }
|
||||
|
||||
public static ModifyMember ModifyMember { get; private set; }
|
||||
|
||||
private static Connection _connection;
|
||||
|
||||
public static string TokenHeaderName = "Authorization";
|
||||
|
||||
/// <summary>
|
||||
/// Actions权限过滤
|
||||
/// </summary>
|
||||
public static GlobalFilter GlobalFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// kafka配置
|
||||
/// </summary>
|
||||
public static Kafka Kafka { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// JWT有效期(分钟=默认120)
|
||||
/// </summary>
|
||||
public static int ExpMinutes { get; private set; } = 120;
|
||||
public static string FullStaticPath { get; private set; } = null;
|
||||
public static string CurrentPath { get; private set; } = null;
|
||||
public static string DownLoadPath { get { return CurrentPath + "\\Download\\"; } }
|
||||
//使用动态分库
|
||||
public static bool UseDynamicShareDB { get; set; }
|
||||
//逻辑删除字段(对应表字段,逻辑删除只会将字段的值设置为1,默认是0)
|
||||
public static string LogicDelField { get; set; } = null;
|
||||
//表的租户字段(使用动态分库功能此字段用不上)
|
||||
public static string TenancyField { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用雪花算法(表的主键字段为bigint类型时启用雪花算法生成唯一id;)
|
||||
/// </summary>
|
||||
public static bool UseSnow { get; set; }
|
||||
|
||||
//是否使用用户权限(限制只能看到指定用户创建的数据,用户管理页面的操作列可以看到此功能,设置为1后生效)
|
||||
public static bool UserAuth { get; set; }
|
||||
|
||||
//2023.12.25所有静态文件访问授权
|
||||
public static bool FileAuth { get; set; }
|
||||
public static void Init(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
services.Configure<Secret>(configuration.GetSection("Secret"));
|
||||
services.Configure<Connection>(configuration.GetSection("Connection"));
|
||||
services.Configure<CreateMember>(configuration.GetSection("CreateMember"));
|
||||
services.Configure<ModifyMember>(configuration.GetSection("ModifyMember"));
|
||||
services.Configure<GlobalFilter>(configuration.GetSection("GlobalFilter"));
|
||||
services.Configure<Kafka>(configuration.GetSection("Kafka"));
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
IWebHostEnvironment environment = provider.GetRequiredService<IWebHostEnvironment>();
|
||||
CurrentPath = Path.Combine(environment.ContentRootPath, "").ReplacePath();
|
||||
|
||||
Secret = provider.GetRequiredService<IOptions<Secret>>().Value;
|
||||
|
||||
//设置修改或删除时需要设置为默认用户信息的字段
|
||||
CreateMember = provider.GetRequiredService<IOptions<CreateMember>>().Value ?? new CreateMember();
|
||||
ModifyMember = provider.GetRequiredService<IOptions<ModifyMember>>().Value ?? new ModifyMember();
|
||||
|
||||
GlobalFilter = provider.GetRequiredService<IOptions<GlobalFilter>>().Value ?? new GlobalFilter();
|
||||
|
||||
GlobalFilter.Actions = GlobalFilter.Actions ?? new string[0];
|
||||
Kafka = provider.GetRequiredService<IOptions<Kafka>>().Value ?? new Kafka();
|
||||
|
||||
_connection = provider.GetRequiredService<IOptions<Connection>>().Value;
|
||||
|
||||
FullStaticPath = Configuration.GetSection("VirtualPath:StaticFile").Value;
|
||||
|
||||
LogicDelField = Configuration["LogicDelField"];
|
||||
|
||||
UseSnow = Configuration["UseSnow"]?.ToString()=="1";
|
||||
UserAuth = Configuration["UserAuth"]?.ToString() == "1";
|
||||
//2023.12.25所有静态文件访问授权
|
||||
FileAuth = Configuration["FileAuth"]?.ToString() == "1";
|
||||
if (LogicDelField == "")
|
||||
{
|
||||
LogicDelField = null;
|
||||
}
|
||||
|
||||
TenancyField = Configuration["TenancyField"];
|
||||
|
||||
if (TenancyField == "")
|
||||
{
|
||||
TenancyField = null;
|
||||
}
|
||||
UseDynamicShareDB = configuration["UseDynamicShareDB"] == "1";
|
||||
|
||||
FullStaticPath = Directory.GetCurrentDirectory() + "\\wwwroot\\lang\\";
|
||||
|
||||
FullStaticPath = FullStaticPath.ReplacePath();
|
||||
Console.WriteLine(FullStaticPath);
|
||||
if (!Directory.Exists(FullStaticPath))
|
||||
{
|
||||
Directory.CreateDirectory(FullStaticPath);
|
||||
}
|
||||
|
||||
ExpMinutes = (configuration["ExpMinutes"] ?? "120").GetInt();
|
||||
|
||||
DBType.Name = _connection.DBType;
|
||||
if (string.IsNullOrEmpty(_connection.DbConnectionString))
|
||||
throw new System.Exception("未配置好数据库默认连接");
|
||||
|
||||
try
|
||||
{
|
||||
_connection.DbConnectionString = _connection.DbConnectionString.DecryptDES(Secret.DB);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(_connection.RedisConnectionString))
|
||||
{
|
||||
try
|
||||
{
|
||||
_connection.RedisConnectionString = _connection.RedisConnectionString.DecryptDES(Secret.Redis);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
}
|
||||
// 多个节点name格式 :["key:key1"]
|
||||
public static string GetSettingString(string key)
|
||||
{
|
||||
return Configuration[key];
|
||||
}
|
||||
// 多个节点,通过.GetSection("key")["key1"]获取
|
||||
public static IConfigurationSection GetSection(string key)
|
||||
{
|
||||
return Configuration.GetSection(key);
|
||||
}
|
||||
}
|
||||
|
||||
public class Connection
|
||||
{
|
||||
public string DBType { get; set; }
|
||||
public bool UseSqlserver2008 { get; set; }
|
||||
public string DbConnectionString { get; set; }
|
||||
public string RedisConnectionString { get; set; }
|
||||
public bool UseRedis { get; set; }
|
||||
public bool UseSignalR { get; set; }
|
||||
}
|
||||
|
||||
public class CreateMember : TableDefaultColumns
|
||||
{
|
||||
}
|
||||
public class ModifyMember : TableDefaultColumns
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class TableDefaultColumns
|
||||
{
|
||||
public string UserIdField { get; set; }
|
||||
public string UserNameField { get; set; }
|
||||
public string DateField { get; set; }
|
||||
}
|
||||
public class GlobalFilter
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public bool Enable { get; set; }
|
||||
public string[] Actions { get; set; }
|
||||
}
|
||||
|
||||
public class Kafka
|
||||
{
|
||||
public bool UseProducer { get; set; }
|
||||
public ProducerSettings ProducerSettings { get; set; }
|
||||
public bool UseConsumer { get; set; }
|
||||
public bool IsConsumerSubscribe { get; set; }
|
||||
public ConsumerSettings ConsumerSettings { get; set; }
|
||||
public Topics Topics { get; set; }
|
||||
}
|
||||
public class ProducerSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
public string SaslMechanism { get; set; }
|
||||
public string SecurityProtocol { get; set; }
|
||||
public string SaslUsername { get; set; }
|
||||
public string SaslPassword { get; set; }
|
||||
}
|
||||
public class ConsumerSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
public string SaslMechanism { get; set; }
|
||||
public string SecurityProtocol { get; set; }
|
||||
public string SaslUsername { get; set; }
|
||||
public string SaslPassword { get; set; }
|
||||
public string GroupId { get; set; }
|
||||
}
|
||||
public class Topics
|
||||
{
|
||||
public string TestTopic { get; set; }
|
||||
}
|
||||
}
|
||||
11
api_sqlsugar/VolPro.Core/Const/ApplicationContentType.cs
Normal file
11
api_sqlsugar/VolPro.Core/Const/ApplicationContentType.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace VolPro.Core.Const
|
||||
{
|
||||
public class ApplicationContentType
|
||||
{
|
||||
public const string FORM = "application/x-www-form-urlencoded; charset=utf-8";
|
||||
public const string STREAM = "application/octet-stream; charset=utf-8";
|
||||
public const string JSON = "application/json; charset=utf-8";
|
||||
public const string XML = "application/xml; charset=utf-8";
|
||||
public const string TEXT = "application/text; charset=utf-8";
|
||||
}
|
||||
}
|
||||
7
api_sqlsugar/VolPro.Core/Const/DataBaseType.cs
Normal file
7
api_sqlsugar/VolPro.Core/Const/DataBaseType.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace VolPro.Core.Const
|
||||
{
|
||||
public static class DBType
|
||||
{
|
||||
public static string Name { get; set; }
|
||||
}
|
||||
}
|
||||
32
api_sqlsugar/VolPro.Core/Const/HtmlElementType.cs
Normal file
32
api_sqlsugar/VolPro.Core/Const/HtmlElementType.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Const
|
||||
{
|
||||
public struct HtmlElementType
|
||||
{
|
||||
public const string drop = "drop";
|
||||
public const string droplist = "droplist";
|
||||
public const string select = "select";
|
||||
public const string selectlist = "selectlist";
|
||||
public const string checkbox = "checkbox";
|
||||
public const string textarea = "textarea";
|
||||
public const string thanorequal = "thanorequal";
|
||||
public const string lessorequal = "lessorequal";
|
||||
|
||||
|
||||
public const string gt = "gt";
|
||||
public const string lt = "lt";
|
||||
public const string GT = ">";
|
||||
public const string LT = "<";
|
||||
public const string like = "like";
|
||||
|
||||
public const string ThanOrEqual = ">=";
|
||||
public const string LessOrequal = "<=";
|
||||
public const string Contains = "in";
|
||||
public const string Equal = "=";
|
||||
public const string NotEqual = "!=";
|
||||
|
||||
}
|
||||
}
|
||||
36
api_sqlsugar/VolPro.Core/Const/Secret.cs
Normal file
36
api_sqlsugar/VolPro.Core/Const/Secret.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace VolPro.Core.Const
|
||||
{
|
||||
/// <summary>
|
||||
/// 加密对应密钥Key
|
||||
/// </summary>
|
||||
public class Secret
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户密码加密key
|
||||
/// </summary>
|
||||
public string User { get; set; }
|
||||
/// <summary>
|
||||
/// 数据库加密key
|
||||
/// </summary>
|
||||
public string DB { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// redis加密key
|
||||
/// </summary>
|
||||
public string Redis { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// jwt加密key
|
||||
/// </summary>
|
||||
public string JWT { get; set; }
|
||||
|
||||
public string Audience { get; set; }
|
||||
public string Issuer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 导出文件加密key
|
||||
/// </summary>
|
||||
public string ExportFile = "C5ABA9E202D94C13A3CB66002BF77FAF";
|
||||
|
||||
}
|
||||
}
|
||||
28
api_sqlsugar/VolPro.Core/Const/SqlDbTypeName.cs
Normal file
28
api_sqlsugar/VolPro.Core/Const/SqlDbTypeName.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Const
|
||||
{
|
||||
public struct SqlDbTypeName
|
||||
{
|
||||
public const string NVarChar = "nvarchar";
|
||||
public const string VarChar = "varchar";
|
||||
public const string NChar = "nchar";
|
||||
public const string Char = "char";
|
||||
public const string Text = "text";
|
||||
public const string Int = "int";
|
||||
public const string BigInt = "bigint";
|
||||
public const string DateTime = "datetime";
|
||||
public const string Date = "date";
|
||||
public const string SmallDateTime = "smalldatetime";
|
||||
public const string SmallDate = "smalldate";
|
||||
public const string Float = "float";
|
||||
public const string Decimal = "decimal";
|
||||
public const string Double = "double";
|
||||
public const string Bit = "bit";
|
||||
public const string Bool = "bool";
|
||||
public const string UniqueIdentifier = "uniqueidentifier";
|
||||
|
||||
}
|
||||
}
|
||||
256
api_sqlsugar/VolPro.Core/Controllers/Basic/ApiBaseController.cs
Normal file
256
api_sqlsugar/VolPro.Core/Controllers/Basic/ApiBaseController.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.Filters;
|
||||
using VolPro.Core.Middleware;
|
||||
using VolPro.Core.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.Controllers.Basic
|
||||
{
|
||||
[JWTAuthorize, ApiController]
|
||||
public class ApiBaseController<IServiceBase> : VolController
|
||||
{
|
||||
protected IServiceBase Service;
|
||||
private WebResponseContent _baseWebResponseContent { get; set; }
|
||||
public ApiBaseController()
|
||||
{
|
||||
}
|
||||
public ApiBaseController(IServiceBase service)
|
||||
{
|
||||
Service = service;
|
||||
}
|
||||
public ApiBaseController(string projectName, string folder, string tablename, IServiceBase service)
|
||||
{
|
||||
Service = service;
|
||||
}
|
||||
[ActionLog("查询")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Search)]
|
||||
[HttpPost, Route("GetPageData")]
|
||||
public virtual ActionResult GetPageData([FromBody] PageDataOptions loadData)
|
||||
{
|
||||
return JsonNormal(InvokeService("GetPageData", new object[] { loadData }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取明细grid分页数据
|
||||
/// </summary>
|
||||
/// <param name="loadData"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("明细查询")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Search)]
|
||||
[HttpPost, Route("GetDetailPage")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult GetDetailPage([FromBody] PageDataOptions loadData)
|
||||
{
|
||||
return Content(InvokeService("GetDetailPage", new object[] { loadData }).Serialize());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传文件
|
||||
/// </summary>
|
||||
/// <param name="fileInput"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("上传文件")]
|
||||
[HttpPost, Route("Upload")]
|
||||
//[ApiActionPermission(ActionPermissionOptions.Upload)]
|
||||
[ApiActionPermission(ActionPermissionOptions.Upload| ActionPermissionOptions.Add | ActionPermissionOptions.Update)]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual IActionResult Upload(IEnumerable<IFormFile> fileInput)
|
||||
{
|
||||
return Json(InvokeService("Upload", new object[] { fileInput }));
|
||||
}
|
||||
/// <summary>
|
||||
/// 下载导入Excel模板
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[ActionLog("下载导入Excel模板")]
|
||||
[HttpGet, Route("DownLoadTemplate")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Import)]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult DownLoadTemplate()
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("DownLoadTemplate", new object[] { }) as WebResponseContent;
|
||||
if (!_baseWebResponseContent.Status) return Json(_baseWebResponseContent);
|
||||
byte[] fileBytes = System.IO.File.ReadAllBytes(_baseWebResponseContent.Data.ToString());
|
||||
return File(
|
||||
fileBytes,
|
||||
System.Net.Mime.MediaTypeNames.Application.Octet,
|
||||
Path.GetFileName(_baseWebResponseContent.Data.ToString())
|
||||
);
|
||||
}
|
||||
/// <summary>
|
||||
/// 导入表数据Excel
|
||||
/// </summary>
|
||||
/// <param name="fileInput"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("导入Excel")]
|
||||
[HttpPost, Route("Import")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Import)]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult Import(List<IFormFile> fileInput)
|
||||
{
|
||||
return Json(InvokeService("Import", new object[] { fileInput }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出文件,返回日期+文件名
|
||||
/// </summary>
|
||||
/// <param name="loadData"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("导出Excel")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Export)]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[HttpPost, Route("Export")]
|
||||
public virtual ActionResult Export([FromBody] PageDataOptions loadData)
|
||||
{
|
||||
var result = InvokeService("Export", new object[] { loadData }) as WebResponseContent;
|
||||
return File(
|
||||
System.IO.File.ReadAllBytes(result.Data.ToString().MapPath()),
|
||||
System.Net.Mime.MediaTypeNames.Application.Octet,
|
||||
Path.GetFileName(result.Data.ToString())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过key删除文件
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
// [ActionLog("删除")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Delete)]
|
||||
[HttpPost, Route("Del")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult Del([FromBody] object[] keys)
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("Del", new object[] { keys, true }) as WebResponseContent;
|
||||
Logger.Info(LoggerType.Del, keys.Serialize(), _baseWebResponseContent.Status ? "Ok" : _baseWebResponseContent.Message);
|
||||
return Json(_baseWebResponseContent);
|
||||
}
|
||||
/// <summary>
|
||||
/// 审核
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
/// [ActionLog("审核")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Audit)]
|
||||
[HttpPost, Route("Audit")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult Audit([FromBody] object[] id, int? auditStatus, string auditReason)
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("Audit", new object[] { id, auditStatus, auditReason }) as WebResponseContent;
|
||||
string msg = _baseWebResponseContent.Status ? ("Ok") : _baseWebResponseContent.Message;
|
||||
Logger.Info($"审核:{id?.Serialize() + "," + (auditStatus ?? -1) + "," + auditReason};{msg}");
|
||||
return Json(_baseWebResponseContent);
|
||||
}
|
||||
/// <summary>
|
||||
/// 撤销、中目审核
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
[ApiActionPermission(Enums.ActionPermissionOptions.CancelAudit)]
|
||||
[HttpPost, Route("cancelAudit")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult CancelAudit([FromBody] AntiData antiData, int status)
|
||||
{
|
||||
var res = InvokeService("CancelAudit", new object[] { antiData, status });
|
||||
return Json(res);
|
||||
}
|
||||
/// <summary>
|
||||
/// 催办
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
// [ApiActionPermission(Enums.ActionPermissionOptions.Audit)]
|
||||
[HttpPost, Route("urgentAudit")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult UrgentAudit([FromBody] object[] id)
|
||||
{
|
||||
var res = InvokeService("UrgentAudit", new object[] { id });
|
||||
return Json(res);
|
||||
}
|
||||
/// <summary>
|
||||
/// 反审核
|
||||
/// </summary>
|
||||
/// <param name="keys"></param>
|
||||
/// <returns></returns>
|
||||
/// [ActionLog("审核")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Audit)]
|
||||
[HttpPost, Route("antiAudit")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult AntiAudit([FromBody] AntiData antiData)
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("AntiAudit", new object[] { antiData }) as WebResponseContent;
|
||||
string msg = _baseWebResponseContent.Status ? ("Ok") : _baseWebResponseContent.Message;
|
||||
Logger.Info($"反审核:{antiData.Serialize()};{msg}");
|
||||
return Json(_baseWebResponseContent);
|
||||
}
|
||||
/// <summary>
|
||||
/// 新增支持主子表
|
||||
/// </summary>
|
||||
/// <param name="saveDataModel"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("新建")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Add)]
|
||||
[HttpPost, Route("Add")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult Add([FromBody] SaveModel saveModel)
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("Add",
|
||||
new Type[] { typeof(SaveModel) },
|
||||
new object[] { saveModel }) as WebResponseContent;
|
||||
Logger.Info(LoggerType.Add, null, _baseWebResponseContent.Status ? "Ok" : _baseWebResponseContent.Message);
|
||||
_baseWebResponseContent.Data = _baseWebResponseContent.Data?.Serialize();
|
||||
return Json(_baseWebResponseContent);
|
||||
}
|
||||
/// <summary>
|
||||
/// 编辑支持主子表
|
||||
/// [ModelBinder(BinderType =(typeof(ModelBinder.BaseModelBinder)))]可指定绑定modelbinder
|
||||
/// </summary>
|
||||
/// <param name="saveDataModel"></param>
|
||||
/// <returns></returns>
|
||||
[ActionLog("编辑")]
|
||||
[ApiActionPermission(ActionPermissionOptions.Update)]
|
||||
[HttpPost, Route("Update")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public virtual ActionResult Update([FromBody] SaveModel saveModel)
|
||||
{
|
||||
_baseWebResponseContent = InvokeService("Update", new object[] { saveModel }) as WebResponseContent;
|
||||
Logger.Info(LoggerType.Edit, null, _baseWebResponseContent.Status ? "Ok" : _baseWebResponseContent.Message);
|
||||
_baseWebResponseContent.Data = _baseWebResponseContent.Data?.Serialize();
|
||||
return Json(_baseWebResponseContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用service方法
|
||||
/// </summary>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
private object InvokeService(string methodName, object[] parameters)
|
||||
{
|
||||
return Service.GetType().GetMethod(methodName).Invoke(Service, parameters);
|
||||
}
|
||||
/// <summary>
|
||||
/// 调用service方法
|
||||
/// </summary>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="types">为要调用重载的方法参数类型:new Type[] { typeof(SaveDataModel)</param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
private object InvokeService(string methodName, Type[] types, object[] parameters)
|
||||
{
|
||||
return Service.GetType().GetMethod(methodName, types).Invoke(Service, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.Controllers.Basic
|
||||
{
|
||||
public class ReportBaseController : VolController
|
||||
{
|
||||
private ReportOption _reportOptions = null;
|
||||
protected ISqlSugarClient DbContext { get; set; }
|
||||
protected ReportOption ReportOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_reportOptions == null)
|
||||
{
|
||||
string code = HttpContext.Request.Query["code"];
|
||||
_reportOptions = DBServerProvider.DbContext.Set<Sys_ReportOptions>().Where(x => x.ReportCode == code)
|
||||
.Select(s => new ReportOption()
|
||||
{
|
||||
ReportOptionsId = s.ReportOptionsId,
|
||||
ReportCode = s.ReportCode,
|
||||
DbService = s.DbService,
|
||||
Sql = s.Options,
|
||||
ParentId = s.ParentId,
|
||||
ReportName = s.ReportName,
|
||||
ReportType = s.ReportType,
|
||||
FilePath = s.FilePath,
|
||||
|
||||
}).ToList().FirstOrDefault();
|
||||
if (_reportOptions == null)
|
||||
{
|
||||
Console.Write($"模板[{code}]不存在");
|
||||
}
|
||||
else
|
||||
{
|
||||
DbContext = DbManger.GetConnection(_reportOptions.DbService);
|
||||
}
|
||||
}
|
||||
return _reportOptions;
|
||||
}
|
||||
}
|
||||
public ReportBaseController()
|
||||
{
|
||||
|
||||
}
|
||||
protected object Data = null;
|
||||
[HttpGet, HttpPost, Route("getTemplateData")]
|
||||
public virtual IActionResult GetTemplateData(string code)
|
||||
{
|
||||
if (ReportOptions == null)
|
||||
{
|
||||
return Error("模板不存在");
|
||||
}
|
||||
string filePath = ReportOptions.FilePath.MapPath(false);
|
||||
string text = System.IO.File.ReadAllText(filePath);
|
||||
|
||||
Data = GetData(code);
|
||||
if (Data != null)
|
||||
{
|
||||
return Success(null, new { text, data = Data });
|
||||
}
|
||||
if (Data == null && !string.IsNullOrEmpty(ReportOptions.Sql))
|
||||
{
|
||||
Data = DbContext.Ado.SqlQuery<object>(ReportOptions.Sql);
|
||||
}
|
||||
return Success(null, new { text, data = new { Table = Data } });
|
||||
}
|
||||
|
||||
protected virtual object GetData(string code)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
//[HttpGet, Route("getData")]
|
||||
//public virtual async Task<IActionResult> GetData(string code)
|
||||
//{
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
public class ReportOption
|
||||
{
|
||||
|
||||
[Key]
|
||||
[Display(Name = "ReportOptionsId")]
|
||||
[Column(TypeName = "uniqueidentifier")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public Guid ReportOptionsId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///报表名称
|
||||
/// </summary>
|
||||
[Display(Name = "报表名称")]
|
||||
[MaxLength(100)]
|
||||
[Column(TypeName = "nvarchar(100)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string ReportName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///报表编码
|
||||
/// </summary>
|
||||
[Display(Name = "报表编码")]
|
||||
[MaxLength(100)]
|
||||
[Column(TypeName = "nvarchar(100)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string ReportCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///所在数据库
|
||||
/// </summary>
|
||||
[Display(Name = "所在数据库")]
|
||||
[MaxLength(100)]
|
||||
[Column(TypeName = "nvarchar(100)")]
|
||||
[Editable(true)]
|
||||
public string DbService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///报表类型
|
||||
/// </summary>
|
||||
[Display(Name = "报表类型")]
|
||||
[MaxLength(100)]
|
||||
[Column(TypeName = "varchar(100)")]
|
||||
[Editable(true)]
|
||||
public string ReportType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///父级id
|
||||
/// </summary>
|
||||
[Display(Name = "父级id")]
|
||||
[Column(TypeName = "uniqueidentifier")]
|
||||
[Editable(true)]
|
||||
public Guid? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///模板文件
|
||||
/// </summary>
|
||||
[Display(Name = "模板文件")]
|
||||
[MaxLength(2000)]
|
||||
[Column(TypeName = "nvarchar(2000)")]
|
||||
[Editable(true)]
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///数据源sql
|
||||
/// </summary>
|
||||
[Display(Name = "数据源sql")]
|
||||
[MaxLength(2000)]
|
||||
[Column(TypeName = "nvarchar(2000)")]
|
||||
[Editable(true)]
|
||||
public string Sql { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
76
api_sqlsugar/VolPro.Core/Controllers/Basic/VolController.cs
Normal file
76
api_sqlsugar/VolPro.Core/Controllers/Basic/VolController.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using VolPro.Core.Filters;
|
||||
|
||||
namespace VolPro.Core.Controllers.Basic
|
||||
{
|
||||
[JWTAuthorize, ApiController]
|
||||
public class VolController : Controller
|
||||
{
|
||||
public VolController()
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 2020.11.21增加json原格式返回数据(默认是驼峰格式)
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="serializerSettings"></param>
|
||||
/// <returns></returns>
|
||||
protected JsonResult JsonNormal(object data, JsonSerializerSettings serializerSettings = null, bool formateDate = true)
|
||||
{
|
||||
serializerSettings = serializerSettings ?? new JsonSerializerSettings();
|
||||
serializerSettings.ContractResolver = null;
|
||||
if (formateDate)
|
||||
{
|
||||
serializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
serializerSettings.Converters.Add(new LongCovert());
|
||||
return Json(data, serializerSettings);
|
||||
}
|
||||
protected IActionResult Success(string message)
|
||||
{
|
||||
return JsonNormal(new { status = true, message });
|
||||
}
|
||||
protected IActionResult Success(string message, object data)
|
||||
{
|
||||
return JsonNormal(new { status = true, message, data });
|
||||
}
|
||||
|
||||
protected IActionResult Error(string message)
|
||||
{
|
||||
return JsonNormal(new { status = false, message });
|
||||
}
|
||||
protected IActionResult Error(string message, object data)
|
||||
{
|
||||
return JsonNormal(new { status = false, message, data });
|
||||
}
|
||||
}
|
||||
public class LongCovert : JsonConverter
|
||||
{
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
long.TryParse(reader.Value.ToString(), out long value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(long) == objectType || typeof(long?) == objectType;
|
||||
}
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
serializer.Serialize(writer, value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Controllers.DynamicController
|
||||
{
|
||||
public class AddControllerChangeProvider : IActionDescriptorChangeProvider
|
||||
{
|
||||
public static AddControllerChangeProvider Instance { get; } = new AddControllerChangeProvider();
|
||||
public CancellationTokenSource TokenSource { get; private set; }
|
||||
public bool HasChanged { get; set; }
|
||||
public IChangeToken GetChangeToken()
|
||||
{
|
||||
TokenSource = new CancellationTokenSource();
|
||||
return new CancellationChangeToken(TokenSource.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Controllers.DynamicController
|
||||
{
|
||||
public class ChangeActionService : IHostedService
|
||||
{
|
||||
private readonly ApplicationPartManager Part;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
public ChangeActionService(IServiceScopeFactory scope, IWebHostEnvironment env)
|
||||
{
|
||||
Part = scope.CreateScope().ServiceProvider.GetService<ApplicationPartManager>();
|
||||
_environment = env;
|
||||
}
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//string rootPath = (_environment.ContentRootPath + "\\plugs").ReplacePath();
|
||||
//foreach (var item in Directory.GetFiles(rootPath).Where(x => x.EndsWith(".dll")))
|
||||
//{
|
||||
// string path = ($"{item}").ReplacePath();
|
||||
// //assemblyList.Add(Assembly.LoadFile(path));
|
||||
// var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
|
||||
// var assemblyPart = new AssemblyPart(assembly);
|
||||
// Part.ApplicationParts.Add(assemblyPart);
|
||||
|
||||
//}
|
||||
|
||||
//AddControllerChangeProvider.Instance.HasChanged = true;
|
||||
//AddControllerChangeProvider.Instance.TokenSource?.Cancel();
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Dapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Enums;
|
||||
|
||||
namespace VolPro.Core.Dapper
|
||||
{
|
||||
public class DapperParseGuidTypeHandler
|
||||
{
|
||||
public static void InitParseGuid()
|
||||
{
|
||||
if (DBType.Name == DbCurrentType.MySql.ToString())
|
||||
{
|
||||
SqlMapper.AddTypeHandler(new DapperParseGuidTypeHandlerMySql());
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid?));
|
||||
}
|
||||
else if (DBType.Name == DbCurrentType.Oracle.ToString())
|
||||
{
|
||||
SqlMapper.AddTypeHandler(new DapperParseGuidTypeHandlerOracle());
|
||||
SqlMapper.RemoveTypeMap(typeof(Guid?));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Dapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Enums;
|
||||
|
||||
namespace VolPro.Core.Dapper
|
||||
{
|
||||
public class DapperParseGuidTypeHandlerMySql : SqlMapper.TypeHandler<Guid?>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, Guid? guid)
|
||||
{
|
||||
parameter.Value = guid.ToString();
|
||||
}
|
||||
|
||||
public override Guid? Parse(object value)
|
||||
{
|
||||
if (value == null || value.ToString() == "")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (value.GetType() == typeof(string))
|
||||
{
|
||||
return new Guid((string)value);
|
||||
}
|
||||
return (Guid)value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Dapper;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Dapper
|
||||
{
|
||||
|
||||
public class DapperParseGuidTypeHandlerOracle : TypeHandlerBase<Guid>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, Guid value)
|
||||
{
|
||||
SetOracleDbTypeOnParameter(parameter, "Raw", 16);
|
||||
parameter.Value = value.ToByteArray();
|
||||
}
|
||||
|
||||
public override Guid Parse(object value)
|
||||
{
|
||||
if (value is byte[] bytearray)
|
||||
{
|
||||
return new Guid(bytearray);
|
||||
}
|
||||
throw new NotImplementedException($"Dont know how to convert a {value.GetType().Name} to a System.Guid.");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TypeHandlerBase<T> : SqlMapper.TypeHandler<T>
|
||||
{
|
||||
private class DictionaryKey
|
||||
{
|
||||
public Type ParameterType { get; set; }
|
||||
public string OracleTypeName { get; set; }
|
||||
}
|
||||
|
||||
private class DictionaryKeyComparer : IEqualityComparer<DictionaryKey>
|
||||
{
|
||||
public bool Equals(DictionaryKey x, DictionaryKey y)
|
||||
{
|
||||
if (x == null && y != null) return false;
|
||||
if (x != null && y != null) return false;
|
||||
|
||||
return x.ParameterType == y.ParameterType && x.OracleTypeName.Equals(y.OracleTypeName);
|
||||
}
|
||||
|
||||
public int GetHashCode(DictionaryKey obj)
|
||||
{
|
||||
return 17 + obj.ParameterType.GetHashCode() * 23 + obj.OracleTypeName.GetHashCode() * 31;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<DictionaryKey, Action<IDbDataParameter>> OracleDbTypeProperty =
|
||||
new ConcurrentDictionary<DictionaryKey, Action<IDbDataParameter>>(new DictionaryKeyComparer());
|
||||
|
||||
protected void SetOracleDbTypeOnParameter(IDbDataParameter parameter, string oracleTypeName, int? length = null)
|
||||
{
|
||||
var setter = OracleDbTypeProperty.GetOrAdd(new DictionaryKey { ParameterType = parameter.GetType(), OracleTypeName = oracleTypeName }, CreateSetTypeAction);
|
||||
setter(parameter);
|
||||
if (length.HasValue)
|
||||
{
|
||||
parameter.Size = length.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Action<IDbDataParameter> CreateSetTypeAction(DictionaryKey key)
|
||||
{
|
||||
Type enumType = key.ParameterType.Assembly.GetType($"{key.ParameterType.Namespace}.OracleDbType");
|
||||
var enumValue = Enum.Parse(enumType, key.OracleTypeName);
|
||||
var inputVariable = Expression.Parameter(typeof(IDbDataParameter));
|
||||
var convertExpression = Expression.Convert(inputVariable, key.ParameterType);
|
||||
|
||||
var expression = Expression.Assign(
|
||||
Expression.PropertyOrField(convertExpression, "OracleDbType"),
|
||||
Expression.Constant(enumValue));
|
||||
|
||||
return Expression.Lambda<Action<IDbDataParameter>>(expression, inputVariable).Compile();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
173
api_sqlsugar/VolPro.Core/Dapper/ISqlDapper.cs
Normal file
173
api_sqlsugar/VolPro.Core/Dapper/ISqlDapper.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Data;
|
||||
//using System.Data.SqlClient;
|
||||
//using System.Linq.Expressions;
|
||||
//using System.Threading.Tasks;
|
||||
//using Dapper;
|
||||
|
||||
//namespace VolPro.Core.Dapper
|
||||
//{
|
||||
// public interface ISqlDapper
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 超时时间(秒)2021.05.05
|
||||
// /// </summary>
|
||||
// /// <param name="timeout"></param>
|
||||
// /// <returns></returns>
|
||||
// ISqlDapper SetTimout(int timeout);
|
||||
// void BeginTransaction(Func<ISqlDapper, bool> action, Action<Exception> error);
|
||||
// List<T> QueryList<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<IEnumerable<T>> QueryListAsync<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// T QueryFirst<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class;
|
||||
|
||||
// Task<T> QueryFirstAsync<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class;
|
||||
|
||||
// Task<dynamic> QueryDynamicFirstAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// dynamic QueryDynamicFirst(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<dynamic> QueryDynamicListAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// List<dynamic> QueryDynamicList(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// object ExecuteScalar(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<object> ExecuteScalarAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<int> ExcuteNonQueryAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
// SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<(IEnumerable<T1>, IEnumerable<T2>)> QueryMultipleAsync<T1, T2>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<T1>, List<T2>) QueryMultiple<T1, T2>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<(IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>)> QueryMultipleAsync<T1, T2, T3>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<T1>, List<T2>, List<T3>) QueryMultiple<T1, T2, T3>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<(IEnumerable<dynamic>, IEnumerable<dynamic>)> QueryDynamicMultipleAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<dynamic>, List<dynamic>) QueryDynamicMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<(IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>, IEnumerable<T4>)> QueryMultipleAsync<T1, T2, T3, T4>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<T1>, List<T2>, List<T3>, List<T4>) QueryMultiple<T1, T2, T3, T4>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<(IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>, IEnumerable<T4>, IEnumerable<T5>)> QueryMultipleAsync<T1, T2, T3, T4, T5>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<T1>, List<T2>, List<T3>, List<T4>, List<T5>) QueryMultiple<T1, T2, T3, T4, T5>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<(IEnumerable<dynamic>, IEnumerable<dynamic>)> QueryDynamicMultipleAsync2(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<dynamic>, List<dynamic>) QueryDynamicMultiple2(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// Task<(IEnumerable<dynamic>, IEnumerable<dynamic>, IEnumerable<dynamic>)> QueryDynamicMultipleAsync3(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
// (List<dynamic>, List<dynamic>, List<dynamic>) QueryDynamicMultiple3(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// Task<(IEnumerable<dynamic>, IEnumerable<dynamic>, IEnumerable<dynamic>, IEnumerable<dynamic>, IEnumerable<dynamic>)> QueryDynamicMultipleAsync5(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
// (List<dynamic>, List<dynamic>, List<dynamic>, List<dynamic>, List<dynamic>) QueryDynamicMultiple5(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <typeparam name="T"></typeparam>
|
||||
// /// <param name="entities"></param>
|
||||
// /// <param name="updateFileds">指定插入的字段</param>
|
||||
// /// <param name="beginTransaction">是否开启事务</param>
|
||||
// /// <returns></returns>
|
||||
// int Add<T>(T entity, Expression<Func<T, object>> updateFileds = null, bool beginTransaction = false);
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <typeparam name="T"></typeparam>
|
||||
// /// <param name="entities"></param>
|
||||
// /// <param name="updateFileds">指定插入的字段</param>
|
||||
// /// <param name="beginTransaction">是否开启事务</param>
|
||||
// /// <returns></returns>
|
||||
// int AddRange<T>(IEnumerable<T> entities, Expression<Func<T, object>> updateFileds = null, bool beginTransaction = false);
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开
|
||||
// /// </summary>
|
||||
// /// <typeparam name="T"></typeparam>
|
||||
// /// <param name="entity">实体必须带主键</param>
|
||||
// /// <param name="updateFileds">指定更新的字段x=new {x.a,x.b}</param>
|
||||
// /// <param name="beginTransaction">是否开启事务</param>
|
||||
// /// <returns></returns>
|
||||
// int Update<T>(T entity, Expression<Func<T, object>> updateFileds = null, bool beginTransaction = false);
|
||||
|
||||
// /// <summary>
|
||||
// /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开
|
||||
// /// </summary>
|
||||
// /// <typeparam name="T"></typeparam>
|
||||
// /// <param name="entity">实体必须带主键</param>
|
||||
// /// <param name="updateFileds">指定更新的字段x=new {x.a,x.b}</param>
|
||||
// /// <param name="beginTransaction">是否开启事务</param>
|
||||
// /// <returns></returns>
|
||||
// int UpdateRange<T>(IEnumerable<T> entities, Expression<Func<T, object>> updateFileds = null, bool beginTransaction = false);
|
||||
|
||||
// int DelWithKey<T>(params object[] keys);
|
||||
// int DelWithKey<T>(bool beginTransaction = false, params object[] keys);
|
||||
// /// <summary>
|
||||
// /// sqlserver批量写入
|
||||
// /// 使用时DataTable table表字段顺序要和数据库字段顺序一致
|
||||
// /// <summary>
|
||||
// /// mysql批量写入
|
||||
// /// </summary>
|
||||
// /// <param name="table"></param>
|
||||
// /// <param name="tableName"></param>
|
||||
// /// <param name="tmpPath">默认当前下载路径</param>
|
||||
// /// <param name="fileName">默认$"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv"</param>
|
||||
// /// <returns></returns>
|
||||
// int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null);
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <typeparam name="T"></typeparam>
|
||||
// /// <param name="entities"></param>
|
||||
// /// <param name="tableName"></param>
|
||||
// /// <param name="columns">所包含的列</param>
|
||||
// /// <param name="sqlBulkCopyOptions"></param>
|
||||
// /// <param name="fileName"></param>
|
||||
// /// <param name="tmpPath"></param>
|
||||
// /// <returns></returns>
|
||||
// int BulkInsert<T>(List<T> entities, string tableName = null,
|
||||
// Expression<Func<T, object>> columns = null,
|
||||
// SqlBulkCopyOptions? sqlBulkCopyOptions = null);
|
||||
|
||||
// /// <summary>
|
||||
// /// 开启事务
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// ISqlDapper BeginTrans();
|
||||
|
||||
// /// <summary>
|
||||
// /// 提交
|
||||
// /// </summary>
|
||||
// void Commit();
|
||||
|
||||
// /// <summary>
|
||||
// /// 回滚
|
||||
// /// </summary>
|
||||
// void Rollback();
|
||||
|
||||
// DataTable QueryDataTable(string sql, object dbParameter, CommandType commandType = CommandType.StoredProcedure);
|
||||
// }
|
||||
//}
|
||||
1002
api_sqlsugar/VolPro.Core/Dapper/SqlDapper.cs
Normal file
1002
api_sqlsugar/VolPro.Core/Dapper/SqlDapper.cs
Normal file
File diff suppressed because it is too large
Load Diff
46
api_sqlsugar/VolPro.Core/Dashboard/DashboardFilter.cs
Normal file
46
api_sqlsugar/VolPro.Core/Dashboard/DashboardFilter.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Dapper;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.ManageUser;
|
||||
|
||||
namespace VolPro.Core.Dashboard
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义设置工作台参数
|
||||
/// </summary>
|
||||
public class DashboardFilter : IDashboardFilterMetaData
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sql">前端配置的sql或存储过程名称</param>
|
||||
/// <param name="dbService">选择的数据库</param>
|
||||
/// <param name="isProc">是否存储过程</param>
|
||||
/// <param name="date1">查询日期1</param>
|
||||
/// <param name="date2">查询日期1</param>
|
||||
/// <param name="filterType">过滤类型:今天、近7天...近一年等</param>
|
||||
/// <returns></returns>
|
||||
public (string sql, List<SugarParameter> parameters) OnActionExecuting(string sql, List<SugarParameter> parameters, string dbService, bool isProc, DateTime? date1, DateTime? date2, string filterType)
|
||||
{
|
||||
//根据不同的类型处理不同的值
|
||||
//if (isProc)
|
||||
//{
|
||||
// if (sql == "存储过程名字")
|
||||
// {
|
||||
// sql = "";
|
||||
// }
|
||||
//}
|
||||
|
||||
////手动在后台设置参数
|
||||
////如:前端sql配置:select * from table where createId=@userId
|
||||
////在这里就可以设置参数
|
||||
//parameters.Add("@userId", UserContext.Current.UserId);
|
||||
|
||||
return (sql, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Dapper;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Dashboard
|
||||
{
|
||||
public interface IDashboardFilterMetaData
|
||||
{
|
||||
(string sql, List<SugarParameter> parameters) OnActionExecuting(string sql, List<SugarParameter> parameters, string dbService, bool isProc, DateTime? date1, DateTime? date2, string filterType);
|
||||
}
|
||||
}
|
||||
11
api_sqlsugar/VolPro.Core/DbManager/DBConnectionAttribute.cs
Normal file
11
api_sqlsugar/VolPro.Core/DbManager/DBConnectionAttribute.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.DBManager
|
||||
{
|
||||
public class DBConnectionAttribute : Attribute
|
||||
{
|
||||
public string DBName { get; set; }
|
||||
}
|
||||
}
|
||||
138
api_sqlsugar/VolPro.Core/DbManager/DBServerProvider.cs
Normal file
138
api_sqlsugar/VolPro.Core/DbManager/DBServerProvider.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Dapper;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
using MySqlConnector;
|
||||
using VolPro.Core.ManageUser;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using VolPro.Entity;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Linq;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using SqlSugar;
|
||||
using VolPro.Core.CacheManager;
|
||||
|
||||
namespace VolPro.Core.DBManager
|
||||
{
|
||||
public partial class DBServerProvider : DbManger
|
||||
{
|
||||
////系统库
|
||||
private static readonly string DefaultConnName = nameof(SysDbContext);
|
||||
|
||||
static DBServerProvider()
|
||||
{
|
||||
}
|
||||
|
||||
public static void SetConnection(string key, string val)
|
||||
{
|
||||
DbRelativeCache.DbContextConnection[key] = val;
|
||||
|
||||
}
|
||||
public static string GetConnectionString(string key)
|
||||
{
|
||||
return DbRelativeCache.DbContextConnection[key ?? DefaultConnName];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统库
|
||||
/// </summary>
|
||||
public static SysDbContext DbContext
|
||||
{
|
||||
get
|
||||
{
|
||||
SysDbContext dbContext = Utilities.HttpContext.Current.RequestServices.GetService(typeof(SysDbContext)) as SysDbContext;
|
||||
return dbContext;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据实体model获取对应EF
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static BaseDbContext GetEntityDbContext<TEntity>()
|
||||
{
|
||||
string dbServer = typeof(TEntity).GetTypeCustomValue<EntityAttribute>(x => x.DBServer);
|
||||
|
||||
return Utilities.HttpContext.Current.RequestServices.GetService(DbRelativeCache.GetDbContextType(dbServer)) as BaseDbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定获取数据库
|
||||
/// </summary>
|
||||
/// <param name="dbService"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDbEntityName(string dbServer)
|
||||
{
|
||||
return DbRelativeCache.GetDbEntityType(dbServer).Name;
|
||||
}
|
||||
|
||||
|
||||
//public static ISqlSugarClient GetSqlSugarClient(string connectionKey = null)
|
||||
//{
|
||||
// return DbManger.GetConnection(connectionKey);
|
||||
//}
|
||||
/// <summary>
|
||||
/// 根据dbcontext名称获取数据库链接
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static string GetDbConnectionString(string key)
|
||||
{
|
||||
if (DbRelativeCache.DbContextConnection.TryGetValue(key, out string connString))
|
||||
{
|
||||
return connString;
|
||||
}
|
||||
throw new Exception($"未配置[{key}]的数据库连接");
|
||||
}
|
||||
|
||||
public static string GetDbConnectionString(Type dbContext)
|
||||
{
|
||||
return GetDbConnectionString(dbContext.Name);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取系统库的字符串连接
|
||||
/// </summary>
|
||||
public static string SysConnectingString
|
||||
{
|
||||
get { return GetDbConnectionString(DefaultConnName); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取业务库的字符串连接
|
||||
/// </summary>
|
||||
public static string ServiceConnectingString
|
||||
{
|
||||
get
|
||||
{
|
||||
//动态无限分库获取用户当前选择的数据库
|
||||
if (AppSetting.UseDynamicShareDB)
|
||||
{
|
||||
return GetDbConnectionString(UserContext.CurrentServiceId.ToString());
|
||||
}
|
||||
return GetDbConnectionString(nameof(ServiceDbContext));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 动态租户分库,获取指定数据库id的链接(2023.11.05)
|
||||
/// </summary>
|
||||
/// <param name="serviceId"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetServiceConnectingString(Guid serviceId)
|
||||
{
|
||||
return DbRelativeCache.DbContextConnection[serviceId.ToString()];
|
||||
}
|
||||
}
|
||||
}
|
||||
284
api_sqlsugar/VolPro.Core/DbManager/DbCopy/DbCopyMySql.cs
Normal file
284
api_sqlsugar/VolPro.Core/DbManager/DbCopy/DbCopyMySql.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Core.DbManager.DbCopy
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// MySQL 数据库复制工具类
|
||||
/// </summary>
|
||||
public class DbCopyMysql
|
||||
{
|
||||
|
||||
public static WebResponseContent CopyDatabase(ISqlSugarClient db,ISqlSugarClient toDbContext, string sourceDatabase,
|
||||
string targetDatabase, bool includeData = true)
|
||||
{
|
||||
var result = new CopyResult();
|
||||
var logs = result.Logs;
|
||||
|
||||
try
|
||||
{
|
||||
// 验证
|
||||
if (string.IsNullOrWhiteSpace(sourceDatabase))
|
||||
throw new ArgumentException("源数据库名不能为空");
|
||||
if (string.IsNullOrWhiteSpace(targetDatabase))
|
||||
throw new ArgumentException("目标数据库名不能为空");
|
||||
if (sourceDatabase.Equals(targetDatabase, StringComparison.OrdinalIgnoreCase))
|
||||
throw new ArgumentException("源和目标数据库不能相同");
|
||||
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 开始复制: {sourceDatabase} -> {targetDatabase}");
|
||||
|
||||
// 检查源数据库
|
||||
if (!DbExists(db, sourceDatabase))
|
||||
throw new Exception($"源数据库 '{sourceDatabase}' 不存在");
|
||||
|
||||
// 检查目标数据库
|
||||
if (DbExists(db, targetDatabase))
|
||||
throw new Exception($"目标数据库 '{targetDatabase}' 已存在");
|
||||
|
||||
// 创建目标数据库
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 创建目标数据库...");
|
||||
CreateDb(db, sourceDatabase, targetDatabase);
|
||||
|
||||
// 复制表
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制表...");
|
||||
CopyTables(db,toDbContext, sourceDatabase, targetDatabase, includeData, result, logs);
|
||||
|
||||
// 复制视图
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制视图...");
|
||||
result.ViewsCount = CopyViews(db,toDbContext, sourceDatabase, targetDatabase, logs);
|
||||
|
||||
// 复制存储过程
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制存储过程...");
|
||||
result.ProceduresCount = CopyRoutines(db, toDbContext, sourceDatabase, targetDatabase, "PROCEDURE", logs);
|
||||
|
||||
// 复制函数
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制函数...");
|
||||
result.FunctionsCount = CopyRoutines(db,toDbContext, sourceDatabase, targetDatabase, "FUNCTION", logs);
|
||||
|
||||
// 复制触发器
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] 复制触发器...");
|
||||
result.TriggersCount = CopyTriggers(db, sourceDatabase, targetDatabase, logs);
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "复制成功";
|
||||
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] ✓ 完成! 表:{result.TablesCount} 视图:{result.ViewsCount} " +
|
||||
$"存储过程:{result.ProceduresCount} 函数:{result.FunctionsCount} 触发器:{result.TriggersCount} " +
|
||||
$"行数:{result.TotalRowsCopied} 耗时:{result.ElapsedSeconds:F2}秒");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
logs.Add($"[{DateTime.Now:HH:mm:ss}] ✗ 错误: {ex.Message}");
|
||||
}
|
||||
WebResponseContent webResponse = new WebResponseContent();
|
||||
webResponse.Status = result.Success;
|
||||
webResponse.Message = result.Message;
|
||||
Logger.Info($"mysql创建数据库:{logs.Serialize()}");
|
||||
return webResponse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库列表
|
||||
/// </summary>
|
||||
public static List<string> GetDatabases(ISqlSugarClient db)
|
||||
{
|
||||
var all = db.Ado.SqlQuery<string>("SHOW DATABASES");
|
||||
var system = new[] { "information_schema", "mysql", "performance_schema", "sys" };
|
||||
return all.Where(x => !system.Contains(x.ToLower())).ToList();
|
||||
}
|
||||
|
||||
private static bool DbExists(ISqlSugarClient db, string name)
|
||||
{
|
||||
return db.Ado.GetInt(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @name",
|
||||
new { name }) > 0;
|
||||
}
|
||||
|
||||
private static void CreateDb(ISqlSugarClient db, string source, string target)
|
||||
{
|
||||
var info = db.Ado.SqlQuerySingle<dynamic>(@"
|
||||
SELECT DEFAULT_CHARACTER_SET_NAME AS Charset, DEFAULT_COLLATION_NAME AS Collation
|
||||
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @name", new { name = source });
|
||||
|
||||
var charset = info?.Charset ?? "utf8mb4";
|
||||
var collation = info?.Collation ?? "utf8mb4_general_ci";
|
||||
|
||||
db.Ado.ExecuteCommand($"CREATE DATABASE `{target}` CHARACTER SET {charset} COLLATE {collation}");
|
||||
}
|
||||
|
||||
private static void CopyTables(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target,
|
||||
bool includeData, CopyResult result, List<string> logs)
|
||||
{
|
||||
//db.Ado.ExecuteCommand($"USE `{source}`");
|
||||
var tables = db.Ado.SqlQuery<string>($"USE `{source}`;SHOW TABLES;");
|
||||
|
||||
db.Ado.ExecuteCommand($"USE `{target}`");
|
||||
db.Ado.ExecuteCommand("SET FOREIGN_KEY_CHECKS = 0");
|
||||
|
||||
foreach (var table in tables)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取建表语句
|
||||
//db.Ado.ExecuteCommand($"USE `{source}`");
|
||||
var row = db.Ado.SqlQuerySingle<dynamic>($"USE `{source}`;SHOW CREATE TABLE `{table}`");
|
||||
var sql = GetValue(row, "Create Table");
|
||||
if (string.IsNullOrEmpty(sql)) continue;
|
||||
|
||||
//创建表
|
||||
toDbContext.Ado.ExecuteCommand($"USE `{target}`");
|
||||
toDbContext.Ado.ExecuteCommand(sql);
|
||||
result.TablesCount++;
|
||||
logs.Add($" ✓ {table}");
|
||||
|
||||
// 复制数据
|
||||
if (includeData)
|
||||
{
|
||||
var rows = db.Ado.ExecuteCommand(
|
||||
$"INSERT INTO `{target}`.`{table}` SELECT * FROM `{source}`.`{table}`");
|
||||
result.TotalRowsCopied += rows;
|
||||
if (rows > 0) logs.Add($" {rows} 行");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Add($" ✗ {table}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
db.Ado.ExecuteCommand("SET FOREIGN_KEY_CHECKS = 1");
|
||||
}
|
||||
|
||||
private static int CopyViews(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target, List<string> logs)
|
||||
{
|
||||
var views = db.Ado.SqlQuery<string>(
|
||||
"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = @db",
|
||||
new { db = source });
|
||||
|
||||
var count = 0;
|
||||
foreach (var view in views)
|
||||
{
|
||||
try
|
||||
{
|
||||
//db.Ado.ExecuteCommand($"USE `{source}`");
|
||||
var row = db.Ado.SqlQuerySingle<dynamic>($"USE `{source}`;SHOW CREATE VIEW `{view}`;");
|
||||
//var row = db.Ado.SqlQuerySingle<dynamic>($"SHOW CREATE VIEW `{view}`;");
|
||||
var sql = GetValue(row, "Create View");
|
||||
if (string.IsNullOrEmpty(sql)) continue;
|
||||
|
||||
sql = sql.Replace($"`{source}`.", $"`{target}`.");
|
||||
sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", "");
|
||||
|
||||
toDbContext.Ado.ExecuteCommand($"USE `{target}`");
|
||||
toDbContext.Ado.ExecuteCommand(sql);
|
||||
count++;
|
||||
logs.Add($" ✓ {view}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Add($" ✗ {view}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static int CopyRoutines(ISqlSugarClient db, ISqlSugarClient toDbContext, string source, string target,
|
||||
string type, List<string> logs)
|
||||
{
|
||||
var routines = db.Ado.SqlQuery<string>(
|
||||
"SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = @db AND ROUTINE_TYPE = @type",
|
||||
new { db = source, type });
|
||||
|
||||
var count = 0;
|
||||
var key = type == "PROCEDURE" ? "Create Procedure" : "Create Function";
|
||||
|
||||
foreach (var routine in routines)
|
||||
{
|
||||
try
|
||||
{
|
||||
//db.Ado.ExecuteCommand($"USE `{source}`");
|
||||
var row = db.Ado.SqlQuerySingle<dynamic>($"USE `{source}`;SHOW CREATE {type} `{routine}`;");
|
||||
// var row = db.Ado.SqlQuerySingle<dynamic>($"SHOW CREATE {type} `{routine}`;");
|
||||
var sql = GetValue(row, key);
|
||||
if (string.IsNullOrEmpty(sql)) continue;
|
||||
|
||||
sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", "");
|
||||
|
||||
toDbContext.Ado.ExecuteCommand($"USE `{target}`");
|
||||
toDbContext.Ado.ExecuteCommand(sql);
|
||||
count++;
|
||||
logs.Add($" ✓ {routine}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Add($" ✗ {routine}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static int CopyTriggers(ISqlSugarClient db, string source, string target, List<string> logs)
|
||||
{
|
||||
var triggers = db.Ado.SqlQuery<string>(
|
||||
"SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = @db",
|
||||
new { db = source });
|
||||
|
||||
var count = 0;
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
try
|
||||
{
|
||||
//db.Ado.ExecuteCommand($"USE `{source}`");
|
||||
var row = db.Ado.SqlQuerySingle<dynamic>($"USE `{source}`;SHOW CREATE TRIGGER `{trigger}`;");
|
||||
//var row = db.Ado.SqlQuerySingle<dynamic>($"SHOW CREATE TRIGGER `{trigger}`;");
|
||||
var sql = GetValue(row, "SQL Original Statement");
|
||||
if (string.IsNullOrEmpty(sql)) continue;
|
||||
|
||||
sql = Regex.Replace(sql, @"DEFINER=`[^`]+`@`[^`]+`\s*", "");
|
||||
|
||||
db.Ado.ExecuteCommand($"USE `{target}`");
|
||||
db.Ado.ExecuteCommand(sql);
|
||||
count++;
|
||||
logs.Add($" ✓ {trigger}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Add($" ✗ {trigger}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static string GetValue(dynamic row, string key)
|
||||
{
|
||||
if (row == null) return "";
|
||||
var dict = row as IDictionary<string, object>;
|
||||
return dict?.ContainsKey(key) == true ? dict[key]?.ToString() ?? "" : "";
|
||||
}
|
||||
}
|
||||
|
||||
public class CopyResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; } = "";
|
||||
public int TablesCount { get; set; }
|
||||
public int ViewsCount { get; set; }
|
||||
public int ProceduresCount { get; set; }
|
||||
public int FunctionsCount { get; set; }
|
||||
public int TriggersCount { get; set; }
|
||||
public long TotalRowsCopied { get; set; }
|
||||
public double ElapsedSeconds { get; set; }
|
||||
public List<string> Logs { get; set; } = new();
|
||||
}
|
||||
}
|
||||
42
api_sqlsugar/VolPro.Core/DbManager/DbCopy/DbCopySqlserver.cs
Normal file
42
api_sqlsugar/VolPro.Core/DbManager/DbCopy/DbCopySqlserver.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
|
||||
namespace VolPro.Core.DbManager.DbCopy
|
||||
{
|
||||
public class DbCopySqlserver
|
||||
{
|
||||
public static string CopyDatabase(string dbEmpty, string dbName)
|
||||
{
|
||||
string DBPath = AppSetting.GetSettingString("DBPath");
|
||||
string DBBackPath = AppSetting.GetSettingString("DBBackPath");
|
||||
|
||||
|
||||
string sql = @$"USE [master]
|
||||
CREATE DATABASE [{dbName}]
|
||||
CONTAINMENT = NONE
|
||||
ON PRIMARY
|
||||
( NAME = N'{dbName}', FILENAME = N'{DBPath}\{dbName}.mdf' , SIZE = 5120KB , FILEGROWTH = 1024KB )
|
||||
LOG ON
|
||||
( NAME = N'{dbName}_log', FILENAME = N'{DBPath}\{dbName}_log.ldf' , SIZE = 2048KB , FILEGROWTH = 10%)
|
||||
|
||||
USE [master]
|
||||
BACKUP DATABASE [{dbEmpty}] TO DISK = N'{DBBackPath}\{dbEmpty}.bak' WITH COPY_ONLY, NOFORMAT, INIT,
|
||||
NAME = N'{dbEmpty}', SKIP, NOREWIND, NOUNLOAD, STATS = 10
|
||||
--还原数据库
|
||||
DECLARE @tomdf NVARCHAR(50)=N'{DBPath}\{dbName}.mdf'
|
||||
DECLARE @tolog NVARCHAR(50)=N'{DBPath}\{dbName}.ldf'
|
||||
RESTORE DATABASE [{dbName}] FROM DISK = N'{DBBackPath}\{dbEmpty}.bak' WITH FILE = 1,
|
||||
MOVE N'{dbEmpty}' TO @tomdf, MOVE N'{dbEmpty}_log' TO @tolog, NOUNLOAD, REPLACE, STATS = 5;
|
||||
|
||||
alter database {dbName} modify file(name={dbEmpty}, newname={dbName});
|
||||
alter database {dbName} modify file(name={dbEmpty}_log, newname={dbName}_log) ";
|
||||
|
||||
return sql;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
11
api_sqlsugar/VolPro.Core/DbManager/DbName.cs
Normal file
11
api_sqlsugar/VolPro.Core/DbManager/DbName.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.DBManage
|
||||
{
|
||||
public struct DbName
|
||||
{
|
||||
public static string Default = "default";
|
||||
}
|
||||
}
|
||||
156
api_sqlsugar/VolPro.Core/DbManager/DbRelativeCache.cs
Normal file
156
api_sqlsugar/VolPro.Core/DbManager/DbRelativeCache.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.ManageUser;
|
||||
using VolPro.Entity.SystemModels;
|
||||
|
||||
namespace VolPro.Core.DBManager
|
||||
{
|
||||
public static class DbRelativeCache
|
||||
{
|
||||
private static Dictionary<string, Type> DbContextTypes = new Dictionary<string, Type>();
|
||||
private static Dictionary<string, Type> DbEntityTypes = new Dictionary<string, Type>();
|
||||
private static Dictionary<string, string> DbTypes = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// 所有数据库链接字符串
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> DbConnection = new Dictionary<string, string>();
|
||||
|
||||
public static Dictionary<string, string> DbContextConnection
|
||||
{
|
||||
get { return DbConnection; }
|
||||
}
|
||||
|
||||
static DbRelativeCache()
|
||||
{
|
||||
InitDbContextType();
|
||||
InitDbEntityType();
|
||||
}
|
||||
/// <summary>
|
||||
/// 缓存分库DbContext
|
||||
/// </summary>
|
||||
public static void InitDbContextType()
|
||||
{
|
||||
var compilationLibrary = DependencyContext
|
||||
.Default
|
||||
.RuntimeLibraries
|
||||
.Where(x => x.Name.EndsWith(".Core") && !x.Serviceable && x.Type != "package" && x.Type == "project");
|
||||
foreach (var _compilation in compilationLibrary)
|
||||
{
|
||||
//加载指定类
|
||||
foreach (var item in AssemblyLoadContext.Default
|
||||
.LoadFromAssemblyName(new AssemblyName(_compilation.Name))
|
||||
.GetTypes().Where(x => x.GetTypeInfo().BaseType != null
|
||||
&& x.BaseType == (typeof(BaseDbContext))))
|
||||
{
|
||||
DbContextTypes[item.Name] = item;
|
||||
//获取数据库链接类型,在appsettings.json中Connection属性添加xxxDbType,前缀与数据库链接一样
|
||||
//ServiceDbContext:"数据库链接字符"=>ServiceDbType:"MsSql";数据库链接类型
|
||||
|
||||
string typeName = item.Name.Replace("DbContext", "").Replace("Entity", "") + "DbType";
|
||||
string dbType = AppSetting.GetSection("Connection")[typeName];
|
||||
DbTypes[item.Name] = dbType ?? DBType.Name;
|
||||
//缓存数据库链接
|
||||
string connectionString = AppSetting.GetSection("Connection")[item.Name];
|
||||
DbConnection.TryAdd(item.Name, connectionString);
|
||||
}
|
||||
}
|
||||
//缓存系统数据库链接
|
||||
DbConnection[nameof(SysDbContext)] = AppSetting.GetSection("Connection")["DbConnectionString"];
|
||||
}
|
||||
/// <summary>
|
||||
/// 缓存分库model基类
|
||||
/// </summary>
|
||||
public static void InitDbEntityType()
|
||||
{
|
||||
var compilationLibrary = DependencyContext
|
||||
.Default
|
||||
.RuntimeLibraries
|
||||
.Where(x => x.Name.EndsWith(".Entity") && !x.Serviceable && x.Type != "package" && x.Type == "project");
|
||||
foreach (var _compilation in compilationLibrary)
|
||||
{
|
||||
//加载指定类
|
||||
foreach (var item in AssemblyLoadContext.Default
|
||||
.LoadFromAssemblyName(new AssemblyName(_compilation.Name))
|
||||
.GetTypes().Where(x => x.GetTypeInfo().BaseType != null
|
||||
&& x.BaseType == (typeof(BaseEntity))))
|
||||
{
|
||||
DbEntityTypes[item.Name] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取数据库的链接类型。如数据库是mysql还是pgsql类型
|
||||
/// </summary>
|
||||
/// <param name="dbService"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDbType(string dbService)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dbService))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
DbTypes.TryGetValue(dbService, out string value);
|
||||
return value;
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据分库名称获取dbcontext
|
||||
/// </summary>
|
||||
/// <param name="dbService"></param>
|
||||
/// <returns></returns>
|
||||
public static Type GetDbContextType(string dbService)
|
||||
{
|
||||
return DbContextTypes[dbService];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据分库名称获取分库model基类
|
||||
/// </summary>
|
||||
/// <param name="dbService"></param>
|
||||
/// <returns></returns>
|
||||
public static Type GetDbEntityType(string dbService)
|
||||
{
|
||||
Type dbContextType = DbContextTypes[dbService];
|
||||
string name = dbContextType.Name.Replace("DbContext", "");
|
||||
return DbEntityTypes[$"{name}Entity"];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据dbtype获取数据库链接
|
||||
/// </summary>
|
||||
/// <param name="dbContextType"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDbConnectionString(Type dbContextType)
|
||||
{
|
||||
return GetDbConnectionString(dbContextType.GetType().Name);
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据dbtype获取数据库链接
|
||||
/// </summary>
|
||||
/// <param name="dbContextType"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDbConnectionString(string dbContextType)
|
||||
{
|
||||
if (dbContextType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
DbConnection.TryGetValue(dbContextType, out string value);
|
||||
if (dbContextType == nameof(ServiceDbContext))
|
||||
{
|
||||
if (AppSetting.UseDynamicShareDB)
|
||||
{
|
||||
return DBServerProvider.GetDbConnectionString(UserContext.CurrentServiceId.ToString());
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Dapper;
|
||||
using VolPro.Core.Enums;
|
||||
|
||||
namespace VolPro.Core.DBManager
|
||||
{
|
||||
public partial class DBServerProvider
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
158
api_sqlsugar/VolPro.Core/DbSqlSugar/DbManger.cs
Normal file
158
api_sqlsugar/VolPro.Core/DbSqlSugar/DbManger.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Core.ManageUser;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity;
|
||||
|
||||
namespace VolPro.Core.DbSqlSugar
|
||||
{
|
||||
public class DbManger
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取业务库对象(租户动态分库)
|
||||
/// </summary>
|
||||
public static ISqlSugarClient ServiceDb
|
||||
{
|
||||
get
|
||||
{
|
||||
var configId = UserContext.CurrentServiceId;
|
||||
if (!Db.IsAnyConnection(configId))
|
||||
{ //用非默认ConfigId进行测试
|
||||
Db.AddConnection(new ConnectionConfig()
|
||||
{
|
||||
ConfigId = configId,
|
||||
ConnectionString = DBServerProvider.ServiceConnectingString,
|
||||
// //2024.01.22增加分库使用不同类型的数据库
|
||||
DbType = SqlSugarDbType.GetType(typeof(ServiceDbContext).Name),// SqlSugar.DbType.SqlServer,
|
||||
IsAutoCloseConnection = true,
|
||||
//https://www.donet5.com/Home/Doc?typeId=1182
|
||||
ConfigureExternalServices = new ConfigureExternalServices()
|
||||
{
|
||||
EntityService = (property, column) =>
|
||||
{
|
||||
if (GetDbType() == DbType.Dm)
|
||||
{
|
||||
var attributes = property.GetCustomAttributes(true);//get all attributes
|
||||
column.DbColumnName = column.DbColumnName.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var result = Db.GetConnection(configId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 动态租户分库,获取指定数据库id的链接(2023.11.05)
|
||||
/// </summary>
|
||||
|
||||
public static ISqlSugarClient GetServiceDb(Guid serviceId)
|
||||
{
|
||||
string configId = serviceId.ToString();
|
||||
if (!Db.IsAnyConnection(configId))
|
||||
{ //用非默认ConfigId进行测试
|
||||
Db.AddConnection(new ConnectionConfig()
|
||||
{
|
||||
ConfigId = configId,
|
||||
ConnectionString = DBServerProvider.GetServiceConnectingString(serviceId),
|
||||
DbType = SqlSugarDbType.GetType(typeof(ServiceDbContext).Name),// SqlSugar.DbType.SqlServer,
|
||||
IsAutoCloseConnection = true,
|
||||
//https://www.donet5.com/Home/Doc?typeId=1182
|
||||
ConfigureExternalServices = new ConfigureExternalServices()
|
||||
{
|
||||
EntityService = (property, column) =>
|
||||
{
|
||||
if (GetDbType() == DbType.Dm)
|
||||
{
|
||||
//var attributes = property.GetCustomAttributes(true);//get all attributes
|
||||
column.DbColumnName = property.Name.ToUpper();// column.DbColumnName.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
var result = Db.GetConnection(configId);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统库:后台异步使用
|
||||
/// </summary>
|
||||
public static SqlSugarScope SysDbContext = new SqlSugarScope(
|
||||
SqlSugarRegister.GetSysConnectionConfig(),
|
||||
db =>
|
||||
{
|
||||
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
Console.WriteLine(sql);//输出sql,查看执行sql 性能无影响
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 根据dbcontext获取链接,
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="dbContextName">
|
||||
///获取系统库 DbManger.GetSqlSugarClient(typeof(SysDbContext).Name)
|
||||
///获取业务库 DbManger.GetSqlSugarClient(typeof(ServiceDbContext).Name)
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static ISqlSugarClient GetSqlSugarClient(string dbContextName = null)
|
||||
{
|
||||
return GetConnection(dbContextName);
|
||||
}
|
||||
public static ISqlSugarClient GetConnection(string dbContextName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dbContextName) || typeof(SysDbContext).Name == dbContextName)
|
||||
{
|
||||
return SysDbContext;
|
||||
}
|
||||
else if (typeof(ServiceDbContext).Name == dbContextName && AppSetting.UseDynamicShareDB)
|
||||
{
|
||||
return ServiceDb;
|
||||
}
|
||||
//其他配置文件里面的自定义数据库链接名称
|
||||
return Db.GetConnection(dbContextName);
|
||||
}
|
||||
|
||||
public static SqlSugarScope Db
|
||||
{
|
||||
get
|
||||
{
|
||||
var obj = AutofacContainerModule.GetService<ISqlSugarClient>();
|
||||
return (SqlSugarScope)obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static DbType GetDbType()
|
||||
{
|
||||
return SqlSugarDbType.GetType();
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据model获取指定dbcontext对象2023.12.17
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static BaseDbContext GetDbContext<TEntity>()
|
||||
{
|
||||
string dbServer = typeof(TEntity).GetTypeCustomValue<EntityAttribute>(x => x.DBServer);
|
||||
|
||||
return HttpContext.Current.RequestServices.GetService(DbRelativeCache.GetDbContextType(dbServer)) as BaseDbContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarDbType.cs
Normal file
69
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarDbType.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
|
||||
namespace VolPro.Core.DbSqlSugar
|
||||
{
|
||||
public static class SqlSugarDbType
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据配置文件中指定的xxDbType获取对应的数据库类型
|
||||
/// </summary>
|
||||
/// <param name="dbContextName"></param>
|
||||
/// <returns></returns>
|
||||
public static DbType GetType(string dbContextName = null)
|
||||
{
|
||||
string _dbType = null;
|
||||
if (!string.IsNullOrEmpty(dbContextName))
|
||||
{
|
||||
if (dbContextName != DBType.Name)
|
||||
{
|
||||
//获取指定的数据库类型
|
||||
_dbType = DbRelativeCache.GetDbType(dbContextName)?.ToLower();
|
||||
}
|
||||
}
|
||||
if (_dbType == null)
|
||||
{
|
||||
_dbType = DBType.Name?.ToString()?.ToLower();
|
||||
}
|
||||
|
||||
DbType dbType = DbType.SqlServer;
|
||||
//配置连接不同的数据库类型,比如同时使用mysql、sqlserver、pgsql数据库
|
||||
switch (_dbType)
|
||||
{
|
||||
//case "mssql":
|
||||
// dbType = DbType.SqlServer;
|
||||
// break;
|
||||
case "mysql":
|
||||
dbType = DbType.MySql;
|
||||
break;
|
||||
case "oracle":
|
||||
dbType = DbType.Oracle;
|
||||
break;
|
||||
case "pgsql":
|
||||
dbType = DbType.PostgreSQL;
|
||||
break;
|
||||
case "kdbndp":
|
||||
dbType = DbType.Kdbndp;
|
||||
break;
|
||||
case "gaussdb":
|
||||
dbType = DbType.GaussDB;
|
||||
break;
|
||||
case "oceanbase":
|
||||
dbType = DbType.OceanBase;
|
||||
break;
|
||||
case "dm":
|
||||
dbType = DbType.Dm;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return dbType;
|
||||
}
|
||||
}
|
||||
}
|
||||
243
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarExtension.cs
Normal file
243
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarExtension.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.UserManager;
|
||||
|
||||
namespace VolPro.Core.DbSqlSugar
|
||||
{
|
||||
public static class SqlSugarExtension
|
||||
{
|
||||
|
||||
public static int Add<T>(this BaseDbContext dbContext, T table, bool saveChange = false) where T : class, new()
|
||||
{
|
||||
dbContext.SqlSugarClient.Insertable(table).AddQueue();
|
||||
if (saveChange)
|
||||
{
|
||||
return dbContext.SqlSugarClient.SaveQueues();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int Add<T>(this ISqlSugarClient sqlSugarClient, T table, bool saveChange = false) where T : class, new()
|
||||
{
|
||||
sqlSugarClient.Insertable(table).AddQueue();
|
||||
if (saveChange)
|
||||
{
|
||||
return sqlSugarClient.SaveQueues();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static async Task<int> AddAsync<T>(this BaseDbContext dbContext, T list, bool saveChange = false) where T : class, new()
|
||||
{
|
||||
dbContext.SqlSugarClient.Insertable(list).AddQueue();
|
||||
if (saveChange)
|
||||
{
|
||||
return await dbContext.SqlSugarClient.SaveQueuesAsync();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int AddRange<T>(this BaseDbContext dbContext, List<T> list, bool saveChange = false) where T : class, new()
|
||||
{
|
||||
if (typeof(T).GetSugarSplitTable() != null)
|
||||
{
|
||||
dbContext.SqlSugarClient.Insertable(list).SplitTable().ExecuteCommand();
|
||||
return list.Count;
|
||||
}
|
||||
dbContext.SqlSugarClient.Insertable(list).AddQueue();
|
||||
if (saveChange)
|
||||
{
|
||||
return dbContext.SqlSugarClient.SaveQueues();
|
||||
}
|
||||
return list.Count;
|
||||
}
|
||||
|
||||
public static async Task<int> AddRangeAsync<T>(this BaseDbContext dbContext, List<T> list, bool saveChange = false) where T : class, new()
|
||||
{
|
||||
if (typeof(T).GetSugarSplitTable() != null)
|
||||
{
|
||||
await dbContext.SqlSugarClient.Insertable(list).SplitTable().ExecuteCommandAsync();
|
||||
return list.Count;
|
||||
}
|
||||
dbContext.SqlSugarClient.Insertable(list).AddQueue();
|
||||
if (saveChange)
|
||||
{
|
||||
return await dbContext.SqlSugarClient.SaveQueuesAsync();
|
||||
}
|
||||
return list.Count;
|
||||
}
|
||||
public static int Update<TSource>(this BaseDbContext dbContext, TSource entity, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(dbContext, new List<TSource>() { entity }, new string[] { }, saveChanges);
|
||||
}
|
||||
public static int Update<TSource>(this BaseDbContext dbContext, TSource entity, Expression<Func<TSource, object>> updateMainFields, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(dbContext, new List<TSource>() { entity }, updateMainFields.GetExpressionProperty(), saveChanges);
|
||||
}
|
||||
|
||||
public static int Update<TSource>(this BaseDbContext dbContext, TSource entity, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(dbContext, new List<TSource>() { entity }, properties, saveChanges);
|
||||
}
|
||||
public static int UpdateRange<TSource>(this BaseDbContext dbContext, IEnumerable<TSource> entities, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(dbContext, entities, new string[] { }, saveChanges);
|
||||
}
|
||||
public static int UpdateRange<TSource>(this BaseDbContext dbContext, IEnumerable<TSource> entities, Expression<Func<TSource, object>> updateMainFields, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return UpdateRange<TSource>(dbContext, entities, updateMainFields.GetExpressionProperty(), saveChanges);
|
||||
}
|
||||
public static int UpdateRange<TSource>(this BaseDbContext dbContext, IEnumerable<TSource> entities, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return dbContext.SqlSugarClient.UpdateRange<TSource>(entities, properties, saveChanges);
|
||||
}
|
||||
public static int Update<TSource>(this ISqlSugarClient sqlSugarClient, TSource entity, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
return sqlSugarClient.UpdateRange<TSource>(new List<TSource>() { entity }, properties, saveChanges);
|
||||
}
|
||||
|
||||
//public static int Update<TSource>(this SqlSugarScope sqlSugarScope, TSource entity, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
//{
|
||||
// return sqlSugarScope.UpdateRange<TSource>(new List<TSource>() { entity }, properties, saveChanges);
|
||||
//}
|
||||
|
||||
public static int UpdateRange<TSource>(this ISqlSugarClient sqlSugarClient, IEnumerable<TSource> entities, string[] properties, bool saveChanges = false) where TSource : class, new()
|
||||
{
|
||||
if (entities.Count() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (properties != null && properties.Length > 0)
|
||||
{
|
||||
PropertyInfo[] entityProperty = typeof(TSource).GetProperties();
|
||||
string keyName = entityProperty.GetKeyName();
|
||||
if (properties.Contains(keyName))
|
||||
{
|
||||
properties = properties.Where(x => x != keyName).ToArray();
|
||||
}
|
||||
properties = properties.Where(x => entityProperty.Select(s => s.Name).Contains(x)).ToArray();
|
||||
}
|
||||
bool splitTable = typeof(TSource).GetSugarSplitTable() != null;
|
||||
IUpdateable<TSource> updateable = null;
|
||||
if (properties == null || properties.Length == 0)
|
||||
{
|
||||
updateable = sqlSugarClient.Updateable<TSource>(entities.ToList());//.AddQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
updateable = sqlSugarClient.Updateable<TSource>(entities.ToList()).UpdateColumns(properties);//.AddQueue();
|
||||
}
|
||||
if (splitTable)
|
||||
{
|
||||
updateable.SplitTable().ExecuteCommand();
|
||||
return entities.Count();
|
||||
}
|
||||
updateable.AddQueue();
|
||||
if (!saveChanges)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return sqlSugarClient.SaveQueues();
|
||||
}
|
||||
|
||||
|
||||
public static Task<T> FirstOrDefaultAsync<T>(this ISugarQueryable<T> queryable)
|
||||
{
|
||||
return queryable.FirstAsync();
|
||||
}
|
||||
|
||||
public static T FirstOrDefault<T>(this ISugarQueryable<T> queryable)
|
||||
{
|
||||
return queryable.First();
|
||||
}
|
||||
|
||||
public static ISugarQueryable<T> Include<T, TProperty>(this ISugarQueryable<T> queryable, Expression<Func<T, TProperty>> incluedProperty) where T : new() where TProperty : new()
|
||||
{
|
||||
return queryable.Includes(incluedProperty);
|
||||
}
|
||||
|
||||
public static T First<T>(this ISugarQueryable<T> queryable)
|
||||
{
|
||||
return queryable.First();
|
||||
}
|
||||
|
||||
public static ISugarQueryable<T> ThenByDescending<T>(this ISugarQueryable<T> queryable, Expression<Func<T, object>> expression)
|
||||
{
|
||||
return queryable.OrderByDescending(expression);
|
||||
}
|
||||
|
||||
|
||||
public static int SaveChanges(this ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
return sqlSugarClient.SaveQueues();
|
||||
}
|
||||
|
||||
public static async Task<int> SaveChangesAsync(this ISqlSugarClient sqlSugarClient)
|
||||
{
|
||||
return await sqlSugarClient.SaveQueuesAsync();
|
||||
}
|
||||
/// <summary>
|
||||
/// 代码生成器自定义sql关联查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <returns></returns>
|
||||
public static ISugarQueryable<TEntity> SetDbSql<TEntity>(this ISqlSugarClient sqlSugarClient, bool filterDeleted = false) where TEntity : class,new()
|
||||
{
|
||||
string sql = TableColumnContext.TableInfo
|
||||
.Where(x => x.TableName == typeof(TEntity).Name)
|
||||
.Select(s => s.DbSql).FirstOrDefault();
|
||||
var query =!string.IsNullOrEmpty(sql)
|
||||
? sqlSugarClient.SqlQueryable<TEntity>(sql)
|
||||
:sqlSugarClient.Queryable<TEntity>();
|
||||
if (filterDeleted && !string.IsNullOrEmpty(AppSetting.LogicDelField))
|
||||
{
|
||||
if (typeof(TEntity).GetProperty(AppSetting.LogicDelField) != null)
|
||||
{
|
||||
var expression = AppSetting.LogicDelField.CreateExpression<TEntity>((int)DelStatus.正常, LinqExpressionType.Equal);
|
||||
return query.Where(expression);
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
public static ISugarQueryable<TEntity> Set<TEntity>(this ISqlSugarClient sqlSugarClient, bool filterDeleted = false) where TEntity : class, new()
|
||||
{
|
||||
return SetDbSql<TEntity>(sqlSugarClient, filterDeleted);
|
||||
}
|
||||
|
||||
public static List<T> QueryList<T>(this ISqlSugarClient sqlSugarClient, string sql, object parameters)
|
||||
{
|
||||
return sqlSugarClient.Ado.SqlQuery<T>(sql, parameters);
|
||||
}
|
||||
public static object ExecuteScalar(this ISqlSugarClient sqlSugarClient, string sql, object parameters)
|
||||
{
|
||||
return sqlSugarClient.Ado.GetScalar(sql, parameters);
|
||||
}
|
||||
public static int ExcuteNonQuery(this ISqlSugarClient sqlSugarClient, string sql, object parameters)
|
||||
{
|
||||
return sqlSugarClient.Ado.ExecuteCommand(sql, parameters);
|
||||
}
|
||||
public static ISqlSugarClient SetTimout(this ISqlSugarClient sqlSugarClient, int time)
|
||||
{
|
||||
// sqlSugarClient.Ado.CommandTimeOut = time;
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
154
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarRegister.cs
Normal file
154
api_sqlsugar/VolPro.Core/DbSqlSugar/SqlSugarRegister.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions;
|
||||
|
||||
namespace VolPro.Core.DbSqlSugar
|
||||
{
|
||||
public static class SqlSugarRegister
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///系统库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ConnectionConfig GetSysConnectionConfig()
|
||||
{
|
||||
var dbType = DbManger.GetDbType();
|
||||
|
||||
return new ConnectionConfig()
|
||||
{
|
||||
DbType = dbType,// SqlSugar.DbType.SqlServer,
|
||||
ConnectionString = DBServerProvider.SysConnectingString,
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigId = "default",
|
||||
MoreSettings = new ConnMoreSettings()
|
||||
{
|
||||
PgSqlIsAutoToLower = false,
|
||||
IsAutoToUpper = false,
|
||||
// DatabaseModel = DbType.PostgreSQL
|
||||
},
|
||||
ConfigureExternalServices = GetConfigureExternalServices()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模板空库(租户动态分才使用)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static ConnectionConfig GetEmptyConnectionConfig()
|
||||
{
|
||||
//Console.WriteLine(AppSetting.GetSection("Connection")["EmptyDbContext"]);
|
||||
var dbType = DbManger.GetDbType();
|
||||
//模板空库(租户动态分才使用)
|
||||
return new ConnectionConfig()
|
||||
{
|
||||
DbType = dbType,// SqlSugar.DbType.SqlServer,
|
||||
ConnectionString = AppSetting.GetSection("Connection")["EmptyDbContext"],
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigId = "EmptyDbContext",
|
||||
MoreSettings = new ConnMoreSettings()
|
||||
{
|
||||
PgSqlIsAutoToLower = false,
|
||||
IsAutoToUpper = false
|
||||
},
|
||||
ConfigureExternalServices = GetConfigureExternalServices()
|
||||
};
|
||||
}
|
||||
|
||||
public static IServiceCollection UseSqlSugar(this IServiceCollection services)
|
||||
{
|
||||
StaticConfig.DynamicExpressionParserType = typeof(DynamicExpressionParser);
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
var dbType = DbManger.GetDbType();
|
||||
|
||||
//缓存所有配置文件的中的数据库链接
|
||||
var configs = DbRelativeCache.DbContextConnection
|
||||
.Where(x => x.Key.EndsWith("DbContext") || x.Key == "default").Select(s => new ConnectionConfig()
|
||||
{
|
||||
//2024.01.22增加分库使用不同类型的数据库
|
||||
DbType = SqlSugarDbType.GetType(s.Key),// SqlSugar.DbType.SqlServer,
|
||||
ConnectionString = s.Value,
|
||||
IsAutoCloseConnection = true,
|
||||
ConfigId = s.Key,
|
||||
MoreSettings = new ConnMoreSettings()
|
||||
{
|
||||
PgSqlIsAutoToLower = false,
|
||||
IsAutoToUpper = false
|
||||
},
|
||||
//https://www.donet5.com/Home/Doc?typeId=1182
|
||||
ConfigureExternalServices = GetConfigureExternalServices()
|
||||
}).ToList();
|
||||
|
||||
configs.Add(GetEmptyConnectionConfig());
|
||||
services.AddSingleton<ISqlSugarClient>(s =>
|
||||
{
|
||||
var sysConfig = GetSysConnectionConfig();
|
||||
SqlSugarScope sqlSugar = new SqlSugarScope(
|
||||
configs,
|
||||
db =>
|
||||
{
|
||||
//业务库日志
|
||||
foreach (var item in configs.Where(x => x.ConfigId?.ToString() != "SysDbContext"))
|
||||
{
|
||||
string id = item.ConfigId.ToString();
|
||||
if (db.GetConnection(id) != null)
|
||||
{
|
||||
db.GetConnection(id).Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
Console.WriteLine(sql);
|
||||
};
|
||||
}
|
||||
};
|
||||
//单例参数配置,所有上下文生效
|
||||
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
Console.WriteLine(sql);
|
||||
};
|
||||
|
||||
});
|
||||
return sqlSugar;
|
||||
});
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置字段全大写
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static ConfigureExternalServices GetConfigureExternalServices()
|
||||
{
|
||||
//https://www.donet5.com/Home/Doc?typeId=1182
|
||||
return new ConfigureExternalServices()
|
||||
{
|
||||
EntityService = (property, column) =>
|
||||
{
|
||||
if (DBType.Name == "DM")
|
||||
{
|
||||
// var attributes = property.GetCustomAttributes(true);//get all attributes
|
||||
column.DbColumnName = property.Name.ToUpper();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
160
api_sqlsugar/VolPro.Core/EFDbContext/BaseDbContext.cs
Normal file
160
api_sqlsugar/VolPro.Core/EFDbContext/BaseDbContext.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Extensions;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.BaseProvider;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
using SqlSugar;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public abstract class BaseDbContext : DbContext
|
||||
{
|
||||
|
||||
|
||||
public ISqlSugarClient SqlSugarClient { get; set; }
|
||||
|
||||
public bool QueryTracking
|
||||
{
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
public BaseDbContext():base() { }
|
||||
|
||||
public ISugarQueryable<TEntity> Set<TEntity>(bool filterDeleted=false) where TEntity : class, new()
|
||||
{
|
||||
return SqlSugarClient.Set<TEntity>(filterDeleted);
|
||||
}
|
||||
|
||||
public int SaveChanges()
|
||||
{
|
||||
return SqlSugarClient.SaveQueues();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void UseDbType(DbContextOptionsBuilder optionsBuilder, string connectionString)
|
||||
{
|
||||
//if (DBType.Name == DbCurrentType.MsSql.ToString())
|
||||
//{
|
||||
// if (AppSetting.UseSqlserver2008)
|
||||
// {
|
||||
// optionsBuilder.ReplaceService<IQueryTranslationPostprocessorFactory, SqlServer2008QueryTranslationPostprocessorFactory>();
|
||||
// }
|
||||
// optionsBuilder.UseSqlServer(connectionString);
|
||||
//}
|
||||
//else if (DBType.Name == DbCurrentType.MySql.ToString())
|
||||
//{
|
||||
// optionsBuilder.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 11)));
|
||||
//}
|
||||
//else if (DBType.Name == DbCurrentType.PgSql.ToString())
|
||||
//{
|
||||
// optionsBuilder.UseNpgsql(connectionString);
|
||||
//}
|
||||
//else if (DBType.Name == DbCurrentType.Oracle.ToString())
|
||||
//{
|
||||
// optionsBuilder.UseOracle(connectionString, b => b.UseOracleSQLCompatibility("11"));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// throw new Exception("数据库未实现");
|
||||
// // optionsBuilder.UseSqlServer(connectionString);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
//protected void OnModelCreating(ModelBuilder modelBuilder, Type type)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// //获取所有类库
|
||||
// var compilationLibrary = DependencyContext
|
||||
// .Default
|
||||
// .CompileLibraries
|
||||
// .Where(x => !x.Serviceable && x.Type != "package" && x.Type == "project");
|
||||
// foreach (var _compilation in compilationLibrary)
|
||||
// {
|
||||
// //加载指定类
|
||||
// AssemblyLoadContext.Default
|
||||
// .LoadFromAssemblyName(new AssemblyName(_compilation.Name))
|
||||
// .GetTypes().Where(x => x.GetTypeInfo().BaseType != null
|
||||
// && x.BaseType == (type)).ToList()
|
||||
// .ForEach(t => { modelBuilder.Entity(t); });
|
||||
// }
|
||||
|
||||
// //Oracle数据库指定表名与列名全部大写
|
||||
// if (DBType.Name == DbCurrentType.Oracle.ToString())
|
||||
// {
|
||||
// foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||
// {
|
||||
// string tableName = entity.GetTableName().ToUpper();
|
||||
// if (tableName.StartsWith("SYS_") || tableName.StartsWith("DEMO_"))
|
||||
// {
|
||||
// entity.SetTableName(entity.GetTableName().ToUpper());
|
||||
// foreach (var property in entity.GetProperties())
|
||||
// {
|
||||
// property.SetColumnName(property.Name.ToUpper());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// //重置系统表名小写,如果是mysql数据库,创建的表名都是小写的,请取消此注释
|
||||
// //foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||
// //{
|
||||
// //
|
||||
// // if (entity.GetTableName().StartsWith("Sys_"))
|
||||
// // {
|
||||
// // Console.WriteLine(entity.GetTableName());
|
||||
// // entity.SetTableName(entity.GetTableName().ToLower());
|
||||
// // }
|
||||
// // //// 重置所有列名
|
||||
// // //foreach (var property in entity.GetProperties())
|
||||
// // //{
|
||||
// // // //StoreObjectIdentifier
|
||||
// // // property.SetColumnName(property.Name);
|
||||
// // //}
|
||||
// //}
|
||||
|
||||
// //插件式开发
|
||||
// //try
|
||||
// //{
|
||||
// // string rootPath = (AppSetting.CurrentPath + "\\plugs").ReplacePath();
|
||||
// // foreach (var item in Directory.GetFiles(rootPath).Where(x => x.EndsWith(".dll")))
|
||||
// // {
|
||||
// // string path = ($"{item}").ReplacePath();
|
||||
// // Assembly.LoadFile(path).GetTypes().Where(x => x.GetTypeInfo().BaseType != null
|
||||
// // && x.BaseType == (type)).ToList()
|
||||
// // .ForEach(t => {
|
||||
// // Console.Write(t.Name);
|
||||
// // modelBuilder.Entity(t);
|
||||
|
||||
// // });
|
||||
// // }
|
||||
// //}
|
||||
// //catch (Exception ex)
|
||||
// //{
|
||||
// // Console.WriteLine($"EF解析类库异常:{ex.Message + ex.StackTrace}");
|
||||
// //}
|
||||
|
||||
// base.OnModelCreating(modelBuilder);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// string mapPath = ($"Log/").MapPath();
|
||||
// Utilities.FileHelper.WriteFile(mapPath, $"syslog_{DateTime.Now.ToString("yyyyMMddHHmmss")}.txt", ex.Message + ex.StackTrace + ex.Source);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
16
api_sqlsugar/VolPro.Core/EFDbContext/DbContext.cs
Normal file
16
api_sqlsugar/VolPro.Core/EFDbContext/DbContext.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public abstract class DbContext//: SqlSugarProvider
|
||||
{
|
||||
//public DbContext(ConnectionConfig connection):base(connection)
|
||||
//{
|
||||
//}
|
||||
}
|
||||
}
|
||||
34
api_sqlsugar/VolPro.Core/EFDbContext/EFLoggerProvider.cs
Normal file
34
api_sqlsugar/VolPro.Core/EFDbContext/EFLoggerProvider.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using VolPro.Core.Utilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public class EFLoggerProvider : ILoggerProvider
|
||||
{
|
||||
public ILogger CreateLogger(string categoryName) => new EFLogger(categoryName);
|
||||
public void Dispose() { }
|
||||
}
|
||||
public class EFLogger : ILogger
|
||||
{
|
||||
private readonly string categoryName;
|
||||
|
||||
public EFLogger(string categoryName) => this.categoryName = categoryName;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => true;
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
//ef core执行数据库查询时的categoryName为Microsoft.EntityFrameworkCore.Database.Command,日志级别为Information
|
||||
if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command"
|
||||
&& logLevel == LogLevel.Information)
|
||||
{
|
||||
var logContent = formatter(state, exception);
|
||||
Console.WriteLine(logContent);
|
||||
}
|
||||
}
|
||||
public IDisposable BeginScope<TState>(TState state) => null;
|
||||
}
|
||||
}
|
||||
30
api_sqlsugar/VolPro.Core/EFDbContext/ServiceDbContext.cs
Normal file
30
api_sqlsugar/VolPro.Core/EFDbContext/ServiceDbContext.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SqlSugar;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public class ServiceDbContext : BaseDbContext, IDependency
|
||||
{
|
||||
private string dbServiceId { get; set; }
|
||||
|
||||
public ServiceDbContext() : base()
|
||||
{
|
||||
this.dbServiceId = dbServiceId;
|
||||
base.SqlSugarClient = DbManger.ServiceDb;
|
||||
}
|
||||
|
||||
|
||||
public ServiceDbContext(string dbServiceId) : base()
|
||||
{
|
||||
this.dbServiceId = dbServiceId;
|
||||
base.SqlSugarClient = DbManger.ServiceDb;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
api_sqlsugar/VolPro.Core/EFDbContext/SysDbContext.cs
Normal file
19
api_sqlsugar/VolPro.Core/EFDbContext/SysDbContext.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using System.Reflection.Emit;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public class SysDbContext : BaseDbContext, IDependency
|
||||
{
|
||||
public SysDbContext() : base() {
|
||||
base.SqlSugarClient = DbManger.SysDbContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
api_sqlsugar/VolPro.Core/EFDbContext/TestDbContext.cs
Normal file
19
api_sqlsugar/VolPro.Core/EFDbContext/TestDbContext.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public class TestDbContext : BaseDbContext, IDependency
|
||||
{
|
||||
|
||||
public TestDbContext() : base() {
|
||||
base.SqlSugarClient = DbManger.GetConnection(nameof(TestDbContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
api_sqlsugar/VolPro.Core/EFDbContext/自定义DbContext.cs
Normal file
19
api_sqlsugar/VolPro.Core/EFDbContext/自定义DbContext.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Entity.SystemModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using VolPro.Core.DbSqlSugar;
|
||||
|
||||
namespace VolPro.Core.EFDbContext
|
||||
{
|
||||
public class 自定义DbContext : BaseDbContext, IDependency
|
||||
{
|
||||
public 自定义DbContext() : base()
|
||||
{
|
||||
base.SqlSugarClient = DbManger.GetConnection(nameof(自定义DbContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
21
api_sqlsugar/VolPro.Core/Enums/ActionPermissionOptions.cs
Normal file
21
api_sqlsugar/VolPro.Core/Enums/ActionPermissionOptions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum ActionPermissionOptions
|
||||
{
|
||||
//注意添加的枚举值一定要是前面的值倍数,即x2
|
||||
Add = 1,
|
||||
Update = 2,
|
||||
Search = 4,
|
||||
Export = 8,
|
||||
Delete = 16,
|
||||
Audit = 32,
|
||||
Upload = 64,//上传文件
|
||||
Import = 128, //导入表数据Excel
|
||||
CancelAudit = 256//撤销审批
|
||||
}
|
||||
}
|
||||
59
api_sqlsugar/VolPro.Core/Enums/ApiMessage.cs
Normal file
59
api_sqlsugar/VolPro.Core/Enums/ApiMessage.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public struct ApiMessage
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 参数有误
|
||||
/// </summary>
|
||||
public const string ParameterError = "请求参数不正确!";
|
||||
/// <summary>
|
||||
/// 没有配置好输入参数
|
||||
/// </summary>
|
||||
public const string NotInputEntity = "没有配置好输入参数!";
|
||||
/// <summary>
|
||||
/// token丢失
|
||||
/// </summary>
|
||||
public const string TokenLose = "token丢失!";
|
||||
|
||||
/// <summary>
|
||||
/// 版本号不能为空
|
||||
/// </summary>
|
||||
|
||||
public const string VersionEmpty = "版本号不能为空!";
|
||||
/// <summary>
|
||||
/// content不能为空
|
||||
/// </summary>
|
||||
|
||||
public const string ContentEmpty = "biz_content不能为空!";
|
||||
/// <summary>
|
||||
/// content不能为空
|
||||
/// </summary>
|
||||
public const string TokenError = "token不正确";
|
||||
|
||||
public const string AccountLocked = "帐号已被锁定!";
|
||||
|
||||
public const string PhoneNoInvalid = "输入的不是手机号";
|
||||
|
||||
|
||||
public const string PINTypeNotRange= "获取验证的类型不正确";
|
||||
public const string OperToBusy = "操作太频繁,请稍后再试";
|
||||
|
||||
public const string SendSTKError = "短信发送异常,请稍后再试";
|
||||
public const string SendSTKSuccess = "短信发送成功";
|
||||
public const string STKNotSend = "请先获取验证码";
|
||||
public const string AccountExists = "手机号已经被注册";
|
||||
|
||||
public const string AccountNotExists = "手机号没有注册";
|
||||
|
||||
public const string PINExpire = "验证码已过期,请重新获取";
|
||||
|
||||
public const string PINError = "验证码不正确";
|
||||
|
||||
public const string ParameterEmpty = "参数不能为空";
|
||||
}
|
||||
}
|
||||
14
api_sqlsugar/VolPro.Core/Enums/ApiStatutsCode.cs
Normal file
14
api_sqlsugar/VolPro.Core/Enums/ApiStatutsCode.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum ApiStatutsCode
|
||||
{
|
||||
False = 0,
|
||||
Ok = 1,
|
||||
TokenExpire = 2
|
||||
|
||||
}
|
||||
}
|
||||
19
api_sqlsugar/VolPro.Core/Enums/AuthData.cs
Normal file
19
api_sqlsugar/VolPro.Core/Enums/AuthData.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum AuthData
|
||||
{
|
||||
全部 = 1,
|
||||
本组织与本角色以及下数据 = 10,
|
||||
本组织及下数据 = 20,
|
||||
本组织数据 = 30,
|
||||
本角色以及下数据 = 40,
|
||||
本角色数据 = 50,
|
||||
仅自己数据 = 60
|
||||
}
|
||||
}
|
||||
22
api_sqlsugar/VolPro.Core/Enums/CPrefix.cs
Normal file
22
api_sqlsugar/VolPro.Core/Enums/CPrefix.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum CPrefix
|
||||
{
|
||||
Role = 0,
|
||||
//UserIDkey
|
||||
UID = 1,
|
||||
/// <summary>
|
||||
/// 头像KEY
|
||||
/// </summary>
|
||||
HDImg = 2,
|
||||
Token = 3,
|
||||
CityList
|
||||
|
||||
}
|
||||
}
|
||||
17
api_sqlsugar/VolPro.Core/Enums/DbCurrentType.cs
Normal file
17
api_sqlsugar/VolPro.Core/Enums/DbCurrentType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum DbCurrentType
|
||||
{
|
||||
Default = 0,
|
||||
MySql = 1,
|
||||
MsSql = 2,//2020.08.08修改sqlserver拼写
|
||||
PgSql = 3,
|
||||
Kdbndp,//人大金仓
|
||||
Oracle, //2022.12.26
|
||||
DM, //2024.02.27
|
||||
}
|
||||
}
|
||||
30
api_sqlsugar/VolPro.Core/Enums/LinqExpressionType.cs
Normal file
30
api_sqlsugar/VolPro.Core/Enums/LinqExpressionType.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum LinqExpressionType
|
||||
{
|
||||
Equal = 0,//=
|
||||
NotEqual = 1,//!=
|
||||
GreaterThan,//>
|
||||
LessThan,//<
|
||||
ThanOrEqual,//>=
|
||||
LessThanOrEqual,//<=
|
||||
In,
|
||||
NotIn,
|
||||
Contains,//Contains
|
||||
Like,//Contains
|
||||
LikeStart,
|
||||
LikeEnd,
|
||||
NotLike,
|
||||
NotContains,//NotContains
|
||||
Null,
|
||||
NotNull,
|
||||
Empty,
|
||||
NotEmpty,
|
||||
NullOrEmpty,
|
||||
NotNullOrEmpty
|
||||
}
|
||||
}
|
||||
54
api_sqlsugar/VolPro.Core/Enums/LoggerType.cs
Normal file
54
api_sqlsugar/VolPro.Core/Enums/LoggerType.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum LoggerType
|
||||
{
|
||||
System = 0,
|
||||
Info,
|
||||
Success,
|
||||
Error,
|
||||
Authorzie,
|
||||
Global,
|
||||
Login,
|
||||
Exception,
|
||||
ApiException,
|
||||
HandleError,
|
||||
OnActionExecuted,
|
||||
GetUserInfo,
|
||||
Edit,
|
||||
Search,
|
||||
Add,
|
||||
Del,
|
||||
AppHome,
|
||||
ApiLogin,
|
||||
ApiPINLogin,
|
||||
ApiRegister,
|
||||
ApiModifyPwd,
|
||||
ApiSendPIN,
|
||||
ApiAuthorize,
|
||||
Ask,
|
||||
JoinMeeting,
|
||||
JoinUs,
|
||||
EditUserInfo,
|
||||
Sell,
|
||||
Buy,
|
||||
ReportPrice,
|
||||
Reply,
|
||||
TechData,
|
||||
TechSecondData,
|
||||
DelPublicQuestion,
|
||||
DelexpertQuestion,
|
||||
CreateTokenError,
|
||||
IPhoneTest,
|
||||
SDKSuccess,
|
||||
SDKSendError,
|
||||
ExpertAuthority,
|
||||
ParEmpty,
|
||||
NoToken,
|
||||
ReplaceToeken,
|
||||
KafkaException
|
||||
}
|
||||
}
|
||||
29
api_sqlsugar/VolPro.Core/Enums/NotificationType.cs
Normal file
29
api_sqlsugar/VolPro.Core/Enums/NotificationType.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知类型
|
||||
/// </summary>
|
||||
public enum NotificationType
|
||||
{
|
||||
审批 = 1,
|
||||
通知 = 2,
|
||||
系统 = 3,
|
||||
抄送
|
||||
}
|
||||
/// <summary>
|
||||
/// 通知对象类型
|
||||
/// </summary>
|
||||
public enum NotificationTarget
|
||||
{
|
||||
用户 = 1,
|
||||
角色 = 2,
|
||||
部门 = 3,
|
||||
岗位 = 4
|
||||
}
|
||||
}
|
||||
12
api_sqlsugar/VolPro.Core/Enums/QueryOrderBy.cs
Normal file
12
api_sqlsugar/VolPro.Core/Enums/QueryOrderBy.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum QueryOrderBy
|
||||
{
|
||||
Desc=1,
|
||||
Asc=2
|
||||
}
|
||||
}
|
||||
31
api_sqlsugar/VolPro.Core/Enums/ResponseType.cs
Normal file
31
api_sqlsugar/VolPro.Core/Enums/ResponseType.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum ResponseType
|
||||
{
|
||||
ServerError = 1,
|
||||
LoginExpiration = 302,
|
||||
ParametersLack = 303,
|
||||
TokenExpiration,
|
||||
PINError,
|
||||
NoPermissions,
|
||||
NoRolePermissions,
|
||||
LoginError,
|
||||
AccountLocked,
|
||||
LoginSuccess,
|
||||
SaveSuccess,
|
||||
AuditSuccess,
|
||||
OperSuccess,
|
||||
RegisterSuccess,
|
||||
ModifyPwdSuccess,
|
||||
EidtSuccess,
|
||||
DelSuccess,
|
||||
NoKey,
|
||||
NoKeyDel,
|
||||
KeyError,
|
||||
Other
|
||||
}
|
||||
}
|
||||
14
api_sqlsugar/VolPro.Core/Enums/UserAgent.cs
Normal file
14
api_sqlsugar/VolPro.Core/Enums/UserAgent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Enums
|
||||
{
|
||||
public enum UserAgent
|
||||
{
|
||||
IOS = 0,
|
||||
Android = 1,
|
||||
Windows = 2,
|
||||
Linux
|
||||
}
|
||||
}
|
||||
44
api_sqlsugar/VolPro.Core/Extensions/AuthorizationResponse.cs
Normal file
44
api_sqlsugar/VolPro.Core/Extensions/AuthorizationResponse.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Core.Extensions
|
||||
{
|
||||
public static class AuthorizationResponse
|
||||
{
|
||||
public static AuthorizationFilterContext FilterResult(
|
||||
this AuthorizationFilterContext context,
|
||||
HttpStatusCode statusCode,
|
||||
string message = null)
|
||||
{
|
||||
context.Result = new ContentResult()
|
||||
{
|
||||
Content = new { message, status = false, code = (int)statusCode }.Serialize(),
|
||||
ContentType = "application/json",
|
||||
StatusCode = (int)statusCode
|
||||
};
|
||||
Logger.Info(LoggerType.ApiAuthorize, message);
|
||||
return context;
|
||||
}
|
||||
public static AuthorizationFilterContext Unauthorized(this AuthorizationFilterContext context, string message = null)
|
||||
{
|
||||
return context.FilterResult(HttpStatusCode.Unauthorized, message);
|
||||
}
|
||||
//不通过JWT验证的,直接将用户信息缓存起来
|
||||
public static void AddIdentity(this AuthorizationFilterContext context, int? userId=null)
|
||||
{
|
||||
int _userId = userId ?? JwtHelper.GetUserId(context.HttpContext.Request.Headers[AppSetting.TokenHeaderName]);
|
||||
if (_userId <= 0) return;
|
||||
//将用户Id缓存到上下文(或者自定一个对象,通过DI以AddScoped方式注入上下文来管理用户信息)
|
||||
var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Jti, _userId.ToString()) };
|
||||
context.HttpContext.User.AddIdentity(new ClaimsIdentity(claims));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using VolPro.Core.Extensions;
|
||||
using System;
|
||||
using VolPro.Core.Configuration;
|
||||
|
||||
namespace VolPro.Core.Extensions.AutofacManager
|
||||
{
|
||||
public class AutofacContainerModule
|
||||
{
|
||||
public static TService GetService<TService>() where TService:class
|
||||
{
|
||||
return typeof(TService).GetService() as TService;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using VolPro.Core.CacheManager;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Const;
|
||||
using VolPro.Core.Dapper;
|
||||
using VolPro.Core.DBManager;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Enums;
|
||||
using VolPro.Core.Extensions.AutofacManager;
|
||||
using VolPro.Core.ManageUser;
|
||||
using VolPro.Core.ObjectActionValidator;
|
||||
using VolPro.Core.Services;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using System.Text;
|
||||
|
||||
namespace VolPro.Core.Extensions
|
||||
{
|
||||
public static class AutofacContainerModuleExtension
|
||||
{
|
||||
// private static bool _isMysql = false;
|
||||
public static IServiceCollection AddModule(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//services.AddSession();
|
||||
//services.AddMemoryCache();
|
||||
//初始化配置文件
|
||||
AppSetting.Init(services, configuration);
|
||||
Type baseType = typeof(IDependency);
|
||||
var compilationLibrary = DependencyContext.Default
|
||||
.RuntimeLibraries
|
||||
.Where(x => !x.Serviceable
|
||||
&& x.Type == "project")
|
||||
.ToList();
|
||||
var count1 = compilationLibrary.Count;
|
||||
List<Assembly> assemblyList = new List<Assembly>();
|
||||
|
||||
foreach (var _compilation in compilationLibrary)
|
||||
{
|
||||
try
|
||||
{
|
||||
assemblyList.Add(AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(_compilation.Name)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(_compilation.Name + ex.Message);
|
||||
}
|
||||
}
|
||||
//插件开发
|
||||
//try
|
||||
//{
|
||||
// var provider = services.BuildServiceProvider();
|
||||
// IWebHostEnvironment webHostEnvironment = provider.GetRequiredService<IWebHostEnvironment>();
|
||||
// string rootPath = (webHostEnvironment.ContentRootPath + "\\plugs").ReplacePath();
|
||||
// foreach (var item in Directory.GetFiles(rootPath).Where(x => x.EndsWith(".dll")))
|
||||
// {
|
||||
// string path = ($"{item}").ReplacePath();
|
||||
// AssemblyName assemblyName = Assembly.LoadFrom(path).GetName(); ;
|
||||
// assemblyList.Add(AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName));
|
||||
// }
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Console.WriteLine($"解析类库异常:{ex.Message + ex.StackTrace}");
|
||||
//}
|
||||
int count = 0;
|
||||
foreach (var _compilation in compilationLibrary)
|
||||
{
|
||||
var types = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(_compilation.Name)).GetTypes();
|
||||
|
||||
var implementedInterfaces = types.Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Length > 0)
|
||||
.Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
|
||||
.Select(t => (serviceType: t.GetInterfaces(), implementationType: t))
|
||||
.ToList();
|
||||
|
||||
foreach (var (serviceType, implementationType) in implementedInterfaces)
|
||||
{
|
||||
if (serviceType.Length==1&& serviceType.Any(x => x == baseType))
|
||||
{
|
||||
services.AddScoped(implementationType);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddScoped(serviceType[0], implementationType);
|
||||
//Console.WriteLine($"注入:{serviceType[0].Name},{implementationType.Name}");
|
||||
}
|
||||
count++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Console.WriteLine(count);
|
||||
services.AddScoped<UserContext>();
|
||||
services.AddScoped<ActionObserver>();
|
||||
services.AddScoped<ObjectModelValidatorState>();
|
||||
// services.AddScoped<WeChatService>();
|
||||
services.AddSingleton(typeof(ICacheService), AppSetting.UseRedis ? typeof(RedisCacheService) : typeof(MemoryCacheService));
|
||||
//if (AppSetting.UseRedis)
|
||||
//{
|
||||
// services.AddSingleton<RedisCacheService>();
|
||||
//}
|
||||
if (DBType.Name == DbCurrentType.PgSql.ToString())
|
||||
{
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
}
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
DapperParseGuidTypeHandler.InitParseGuid();
|
||||
DbCache.Init();
|
||||
//kafka注入
|
||||
//if (AppSetting.Kafka.UseConsumer)
|
||||
// builder.RegisterType<KafkaConsumer<string, string>>().As<IKafkaConsumer<string, string>>().SingleInstance();
|
||||
//if (AppSetting.Kafka.UseProducer)
|
||||
// builder.RegisterType<KafkaProducer<string, string>>().As<IKafkaProducer<string, string>>().SingleInstance();
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.Extensions.AutofacManager
|
||||
{
|
||||
public interface IDependency
|
||||
{
|
||||
}
|
||||
}
|
||||
23
api_sqlsugar/VolPro.Core/Extensions/CacheKeyExtensions.cs
Normal file
23
api_sqlsugar/VolPro.Core/Extensions/CacheKeyExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using VolPro.Core.Enums;
|
||||
using System;
|
||||
|
||||
namespace VolPro.Core.Extensions
|
||||
{
|
||||
public static class CacheKeyExtensions
|
||||
{
|
||||
public static string GetKey(this CPrefix prefix, object value)
|
||||
{
|
||||
return prefix.ToString() + value;
|
||||
}
|
||||
|
||||
public static string GetUserIdKey(this int userId)
|
||||
{
|
||||
return CPrefix.UID.ToString() + userId;
|
||||
}
|
||||
|
||||
public static string GetRoleIdKey(this int roleId)
|
||||
{
|
||||
return CPrefix.Role.ToString() + roleId;
|
||||
}
|
||||
}
|
||||
}
|
||||
313
api_sqlsugar/VolPro.Core/Extensions/ConvertJsonExtension.cs
Normal file
313
api_sqlsugar/VolPro.Core/Extensions/ConvertJsonExtension.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
|
||||
namespace VolPro.Core.Extensions
|
||||
{
|
||||
public static class ConvertJsonExtension
|
||||
{
|
||||
#region 私有方法
|
||||
/// <summary>
|
||||
/// 过滤特殊字符
|
||||
/// </summary>
|
||||
/// <param name="s">字符串</param>
|
||||
/// <returns>json字符串</returns>
|
||||
private static string String2Json(String s)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
char c = s.ToCharArray()[i];
|
||||
switch (c)
|
||||
{
|
||||
case '\"':
|
||||
sb.Append("\\\""); break;
|
||||
case '\\':
|
||||
sb.Append("\\\\"); break;
|
||||
case '/':
|
||||
sb.Append("\\/"); break;
|
||||
case '\b':
|
||||
sb.Append("\\b"); break;
|
||||
case '\f':
|
||||
sb.Append("\\f"); break;
|
||||
case '\n':
|
||||
sb.Append("\\n"); break;
|
||||
case '\r':
|
||||
sb.Append("\\r"); break;
|
||||
case '\t':
|
||||
sb.Append("\\t"); break;
|
||||
default:
|
||||
sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// 格式化字符型、日期型、布尔型
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private static string StringFormat(string str, Type type)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
str = String2Json(str);
|
||||
str = "\"" + str + "\"";
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
str = "\"" + str + "\"";
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
str = str.ToLower();
|
||||
}
|
||||
else if (type != typeof(string) && string.IsNullOrEmpty(str))
|
||||
{
|
||||
str = "\"" + str + "\"";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region list转换成JSON
|
||||
/// <summary>
|
||||
/// list转换为Json
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
public static string ListToJson<T>(this IList<T> list)
|
||||
{
|
||||
object obj = list[0];
|
||||
return ListToJson<T>(list, obj.GetType().Name);
|
||||
}
|
||||
/// <summary>
|
||||
/// list转换为json
|
||||
/// </summary>
|
||||
/// <typeparam name="T1"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="p"></param>
|
||||
/// <returns></returns>
|
||||
private static string ListToJson<T>(this IList<T> list, string JsonName)
|
||||
{
|
||||
return list.Serialize();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 对象转换为Json
|
||||
/// <summary>
|
||||
/// 对象转换为json
|
||||
/// </summary>
|
||||
/// <param name="jsonObject">json对象</param>
|
||||
/// <returns>json字符串</returns>
|
||||
public static string ToJson(this object jsonObject)
|
||||
{
|
||||
if (jsonObject==null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return jsonObject.Serialize();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 对象集合转换为json
|
||||
/// <summary>
|
||||
/// 对象集合转换为json
|
||||
/// </summary>
|
||||
/// <param name="array">对象集合</param>
|
||||
/// <returns>json字符串</returns>
|
||||
public static string ToJson(this IEnumerable array)
|
||||
{
|
||||
string jsonString = "{";
|
||||
foreach (object item in array)
|
||||
{
|
||||
jsonString += ToJson(item) + ",";
|
||||
}
|
||||
jsonString.Remove(jsonString.Length - 1, jsonString.Length);
|
||||
return jsonString + "]";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 普通集合转换Json
|
||||
/// <summary>
|
||||
/// 普通集合转换Json
|
||||
/// </summary>
|
||||
/// <param name="array">集合对象</param>
|
||||
/// <returns>Json字符串</returns>
|
||||
public static string ToArrayString(this IEnumerable array)
|
||||
{
|
||||
string jsonString = "[";
|
||||
foreach (object item in array)
|
||||
{
|
||||
jsonString = ToJson(item.ToString()) + ",";
|
||||
}
|
||||
jsonString.Remove(jsonString.Length - 1, jsonString.Length);
|
||||
return jsonString + "]";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DataSet转换为Json
|
||||
/// <summary>
|
||||
/// DataSet转换为Json
|
||||
/// </summary>
|
||||
/// <param name="dataSet">DataSet对象</param>
|
||||
/// <returns>Json字符串</returns>
|
||||
public static string ToJson(this DataSet dataSet)
|
||||
{
|
||||
string jsonString = "{";
|
||||
foreach (DataTable table in dataSet.Tables)
|
||||
{
|
||||
jsonString += "\"" + table.TableName + "\":" + ToJson(table) + ",";
|
||||
}
|
||||
jsonString = jsonString.TrimEnd(',');
|
||||
return jsonString + "}";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Datatable转换为Json
|
||||
/// <summary>
|
||||
/// Datatable转换为Json
|
||||
/// </summary>
|
||||
/// <param name="table">Datatable对象</param>
|
||||
/// <returns>Json字符串</returns>
|
||||
public static string ToJson(this DataTable dt)
|
||||
{
|
||||
StringBuilder jsonString = new StringBuilder();
|
||||
jsonString.Append("[");
|
||||
DataRowCollection drc = dt.Rows;
|
||||
for (int i = 0; i < drc.Count; i++)
|
||||
{
|
||||
jsonString.Append("{");
|
||||
for (int j = 0; j < dt.Columns.Count; j++)
|
||||
{
|
||||
string strKey = dt.Columns[j].ColumnName;
|
||||
string strValue = drc[i][j].ToString();
|
||||
Type type = dt.Columns[j].DataType;
|
||||
jsonString.Append("\"" + strKey + "\":");
|
||||
strValue = StringFormat(strValue, type);
|
||||
if (j < dt.Columns.Count - 1)
|
||||
{
|
||||
jsonString.Append(strValue + ",");
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonString.Append(strValue);
|
||||
}
|
||||
}
|
||||
jsonString.Append("},");
|
||||
}
|
||||
jsonString.Remove(jsonString.Length - 1, 1);
|
||||
jsonString.Append("]");
|
||||
return jsonString.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// DataTable转换为Json
|
||||
/// </summary>
|
||||
public static string ToJson(this DataTable dt, string jsonName)
|
||||
{
|
||||
StringBuilder Json = new StringBuilder();
|
||||
if (string.IsNullOrEmpty(jsonName))
|
||||
jsonName = dt.TableName;
|
||||
Json.Append("{\"" + jsonName + "\":[");
|
||||
if (dt.Rows.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < dt.Rows.Count; i++)
|
||||
{
|
||||
Json.Append("{");
|
||||
for (int j = 0; j < dt.Columns.Count; j++)
|
||||
{
|
||||
Type type = dt.Rows[i][j].GetType();
|
||||
Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type));
|
||||
if (j < dt.Columns.Count - 1)
|
||||
{
|
||||
Json.Append(",");
|
||||
}
|
||||
}
|
||||
Json.Append("}");
|
||||
if (i < dt.Rows.Count - 1)
|
||||
{
|
||||
Json.Append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
Json.Append("]}");
|
||||
return Json.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataReader转换为Json
|
||||
/// <summary>
|
||||
/// DataReader转换为Json
|
||||
/// </summary>
|
||||
/// <param name="dataReader">DataReader对象</param>
|
||||
/// <returns>Json字符串</returns>
|
||||
public static string ReaderJson(this IDataReader dataReader)
|
||||
{
|
||||
StringBuilder jsonString = new StringBuilder();
|
||||
Dictionary<string, Type> ModelField = new Dictionary<string, Type>();
|
||||
for (int i = 0; i < dataReader.FieldCount; i++)
|
||||
{
|
||||
ModelField.Add(dataReader.GetName(i), dataReader.GetFieldType(i));
|
||||
}
|
||||
jsonString.Append("[");
|
||||
while (dataReader.Read())
|
||||
{
|
||||
jsonString.Append("{");
|
||||
foreach (KeyValuePair<string, Type> keyVal in ModelField)
|
||||
{
|
||||
Type type = keyVal.Value;
|
||||
string strKey = keyVal.Key;
|
||||
string strValue = dataReader[strKey].ToString();
|
||||
jsonString.Append("\"" + strKey + "\":");
|
||||
strValue = StringFormat(strValue, type);
|
||||
jsonString.Append(strValue + ",");
|
||||
}
|
||||
jsonString.Remove(jsonString.Length - 1, 1);
|
||||
jsonString.Append("},");
|
||||
}
|
||||
dataReader.Close();
|
||||
jsonString.Remove(jsonString.Length - 1, 1);
|
||||
jsonString.Append("]");
|
||||
return jsonString.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public static T DeserializeObject<T>(this string entityString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(entityString))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
if (entityString == "{}")
|
||||
{
|
||||
entityString = "[]";
|
||||
}
|
||||
var settings = new JsonSerializerSettings();
|
||||
settings.Converters.Add(new LongCovert());
|
||||
return JsonConvert.DeserializeObject<T>(entityString, settings);
|
||||
}
|
||||
|
||||
public static string Serialize(this object obj, JsonSerializerSettings formatDate = null)
|
||||
{
|
||||
if (obj == null) return null;
|
||||
formatDate = formatDate ?? new JsonSerializerSettings
|
||||
{
|
||||
DateFormatString = "yyyy-MM-dd HH:mm:ss"
|
||||
};
|
||||
formatDate.Converters.Add(new LongCovert());
|
||||
return JsonConvert.SerializeObject(obj, formatDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
api_sqlsugar/VolPro.Core/Extensions/EntityCustom.cs
Normal file
54
api_sqlsugar/VolPro.Core/Extensions/EntityCustom.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.ManageUser;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.Extensions
|
||||
{
|
||||
|
||||
public static class EntityCustom
|
||||
{
|
||||
private static string[] DefaultFields = null;// new string[] { "你的字段1", "你的字段2" };
|
||||
|
||||
/// <summary>
|
||||
/// 功能作用:给所有表包括DefaultFields数组的字段都统一设置默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="entity"></param>
|
||||
public static void SetValue<T>(this T entity) where T : class
|
||||
{
|
||||
if (DefaultFields == null) return;
|
||||
var properties = typeof(T).GetProperties().Where(x => DefaultFields.Contains(x.Name));
|
||||
if (!properties.Any()) return;
|
||||
|
||||
//如果某些表有相同的字段但不需要设置值,在这此执行
|
||||
//if (nameof(T)==nameof(表model))
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//在这里给表对象设置默认值
|
||||
foreach (var item in properties)
|
||||
{
|
||||
//UserContext.Current.UserInfo获取用户信息,也可以在userinfo里面加其他字段
|
||||
//usercontext是已缓存的对象,尽量不要在这里查询数据库
|
||||
switch (item.Name)
|
||||
{
|
||||
case "你的字段1":
|
||||
//可以从UserContext.Current.UserInfo取值
|
||||
item.SetValue(entity, "你的字段1值");
|
||||
break;
|
||||
case "你的字段2":
|
||||
item.SetValue(entity, "你的字段2值");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user