添加项目文件。

This commit is contained in:
fuenmao
2025-09-29 13:48:36 +08:00
parent f61e922f0c
commit 5dc1780ef1
407 changed files with 63703 additions and 0 deletions
@@ -0,0 +1,199 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
using System.Collections;
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// CodeFirst功能类
/// </summary>
[SuppressSniffer]
public static class CodeFirstUtils
{
/// <summary>
/// CodeFirst生成数据库表结构和种子数据
/// </summary>
/// <param name="options">codefirst选项</param>
/// <param name="assemblyName">程序集名称</param>
public static void CodeFirst(BaseOptions options, string assemblyName)
{
var appName = assemblyName.Split(",")[0];
if (options.InitTable)//如果需要初始化表结构
{
Console.WriteLine($"开始初始化{appName}数据库表结构");
InitTable(assemblyName);
}
if (options.InitSeedData)
{
Console.WriteLine($"开始初始化{appName}数据库种子数据");
InitSeedData(assemblyName);
}
}
/// <summary>
/// 初始化数据库表结构
/// </summary>
/// <param name="assemblyName">程序集名称</param>
private static void InitTable(string assemblyName)
{
// 获取所有实体表-初始化表结构
var entityTypes = App.EffectiveTypes.Where(u =>
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.Assembly.FullName == assemblyName);
if (!entityTypes.Any()) return;//没有就退出
foreach (var entityType in entityTypes)
{
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取SqlSugar多租户特性
var ignoreInit = entityType.GetCustomAttribute<IgnoreInitTableAttribute>();//获取忽略初始化特性
if (ignoreInit != null) continue;//如果有忽略初始化特性
if (tenantAtt == null) continue;//如果没有租户特性就下一个
var db = DbContext.DB.GetConnectionScope(tenantAtt.configId.ToString());//获取数据库对象
var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>();//获取自动分表特性
if (splitTable == null)//如果特性是空
db.CodeFirst.InitTables(entityType);//普通创建
else
db.CodeFirst.SplitTables().InitTables(entityType);//自动分表创建
}
}
/// <summary>
/// 初始化种子数据
/// </summary>
/// <param name="assemblyName">程序集名称</param>
private static void InitSeedData(string assemblyName)
{
// 获取所有种子配置-初始化数据
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && u is { IsAbstract: false, IsClass: true }
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))) && u.Assembly.FullName == assemblyName);
if (!seedDataTypes.Any()) return;
foreach (var seedType in seedDataTypes)//遍历种子类
{
//使用与指定参数匹配程度最高的构造函数来创建指定类型的实例。
var instance = Activator.CreateInstance(seedType);
//获取SeedData方法
var hasDataMethod = seedType.GetMethod("SeedData");
//判断是否有种子数据
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
if (seedData == null) continue;//没有种子数据就下一个
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();//获取实体类型
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取SqlSugar租户特性
if (tenantAtt == null) continue;//如果没有租户特性就下一个
var db = DbContext.DB.GetConnectionScope(tenantAtt.configId.ToString());//获取数据库对象
var config = DbContext.DB_CONFIGS.FirstOrDefault(u => u.ConfigId == tenantAtt.configId.ToString());//获取数据库配置
// var seedDataTable = seedData.ToList().ToDataTable();//获取种子数据:已弃用
var entityInfo = db.EntityMaintenance.GetEntityInfo(entityType);
// seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;//获取表名
var ignoreAdd = hasDataMethod.GetCustomAttribute<IgnoreSeedDataAddAttribute>();//读取忽略插入特性
var ignoreUpdate = hasDataMethod.GetCustomAttribute<IgnoreSeedDataUpdateAttribute>();//读取忽略更新特性
if (entityInfo.Columns.Any(u => u.IsPrimarykey))//判断种子数据是否有主键
{
// 按主键进行批量增加和更新
var storage = db.StorageableByObject(seedData.ToList()).ToStorage();
if (ignoreAdd == null) storage.AsInsertable.ExecuteCommand();//执行插入
if (ignoreUpdate == null) storage.AsUpdateable.ExecuteCommand();//只有没有忽略更新的特性才执行更新
}
else// 没有主键或者不是预定义的主键(有重复的可能)
{
//全量插入
// 无主键则只进行插入
if (!db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any() && ignoreAdd == null)
db.InsertableByObject(seedData.ToList()).ExecuteCommand();
}
}
}
/// <summary>
/// 判断类型是否实现某个泛型
/// </summary>
/// <param name="type">类型</param>
/// <param name="generic">泛型类型</param>
/// <returns>bool</returns>
public static bool HasImplementedRawGeneric(this Type type, Type generic)
{
// 检查接口类型
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
if (isTheRawGenericType) return true;
// 检查类型
while (type != null && type != typeof(object))
{
isTheRawGenericType = IsTheRawGenericType(type);
if (isTheRawGenericType) return true;
type = type.BaseType ?? default;
}
return false;
// 判断逻辑
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
}
/// <summary>
/// List转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
var result = new DataTable();
if (list.Count > 0)
{
// result.TableName = list[0].GetType().Name; // 表名赋值
var propertys = list[0].GetType().GetProperties();
foreach (var pi in propertys)
{
var colType = pi.PropertyType;
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
colType = colType.GetGenericArguments()[0];
}
if (IsIgnoreColumn(pi))
continue;
if (IsJsonColumn(pi))//如果是json特性就是sting类型
colType = typeof(string);
result.Columns.Add(pi.Name, colType);
}
for (var i = 0; i < list.Count; i++)
{
var tempList = new ArrayList();
foreach (var pi in propertys)
{
if (IsIgnoreColumn(pi))
continue;
var obj = pi.GetValue(list[i], null);
if (IsJsonColumn(pi))//如果是json特性就是转化为json格式
obj = obj?.ToJson();//如果json字符串是空就传null
tempList.Add(obj);
}
var array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
/// <summary>
/// 排除SqlSugar忽略的列
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
private static bool IsIgnoreColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore);
return sc != null;
}
private static bool IsJsonColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsJson);
return sc != null;
}
}
@@ -0,0 +1,42 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// SqlSugar表信息
/// </summary>
public class SqlSugarTableInfo
{
/// <summary>
/// 所属库
///</summary>
public virtual string ConfigId { get; set; }
/// <summary>
/// 表名称
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 实体名
/// </summary>
public string EntityName { get; set; }
/// <summary>
/// 表注释
/// </summary>
public string TableDescription { get; set; }
/// <summary>
/// 表字段
/// </summary>
public List<SqlSugarColumnInfo> Columns { get; set; }
}
@@ -0,0 +1,37 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// SqlSugar字段信息
/// </summary>
public class SqlSugarColumnInfo
{
/// <summary>
/// 字段名称
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 是否主键
/// </summary>
public bool IsPrimaryKey { get; set; }
/// <summary>
/// 字段类型
/// </summary>
public string DataType { get; set; }
/// <summary>
/// 字段注释
/// </summary>
public string ColumnDescription { get; set; }
}
+154
View File
@@ -0,0 +1,154 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
using System.Text.RegularExpressions;
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// 种子数据工具类
/// </summary>
public class SeedDataUtil
{
public static List<T> GetSeedData<T>(string jsonName)
{
var seedData = new List<T>();//种子数据结果
var basePath = AppContext.BaseDirectory;//获取项目目录
var json = basePath.CombinePath("SeedData", "Json", jsonName);//获取文件路径
var dataString = FileHelper.ReadFile(json);//读取文件
if (!string.IsNullOrEmpty(dataString))//如果有内容
{
//字段没有数据的替换成null
dataString = dataString.Replace("\"\"", "null");
#region 针对导出的json字符串嵌套json字符串如 "DefaultDataScope": "{\"Level\":5,\"ScopeCategory\":\"SCOPE_ALL\",\"ScopeDefineOrgIdList\":[]}"
//正则匹配"ConfigValue": "[{开头的字符串以]"结尾
var matcheDefaultDataScope = Regex.Matches(dataString, "\"DefaultDataScope\": \"\\{.*?\\}\"");
foreach (Match match in matcheDefaultDataScope)
{
//获取匹配的值
var value = match.Value;
//将匹配的值替换成"ConfigValue": "{XXX}"
//字符串是\"的替换成"
var newValue = value.Replace("\\\"", "\"");
//字符串是\{替换成{
newValue = newValue.Replace("\"{", "{");
//字符串是}"的替换成}
newValue = newValue.Replace("}\"", "}");
dataString = dataString.Replace(value, newValue);
}
#endregion 针对导出的json字符串嵌套json字符串如 "DefaultDataScope": "{\"Level\":5,\"ScopeCategory\":\"SCOPE_ALL\",\"ScopeDefineOrgIdList\":[]}"
#region Sys_Org
//如果T是Sys_Org
var nameofT = typeof(T).Name;//获取类型名称
if (nameofT == "SysOrg")
{
//字段是"[的替换成[
dataString = dataString.Replace("\"[", "[");
//字段是]"的替换成]
dataString = dataString.Replace("]\"", "]");
}
#endregion
//将json字符串转为实体,这里ExtJson可以正常转换为字符串
var seedDataRecord1 = dataString.ToJsonEntity<SeedDataRecords<T>>();
//正则匹配"ConfigValue": "[{开头的字符串以]"结尾
var matches = Regex.Matches(dataString, "\"ConfigValue\": \"\\[\\{.*?\\}\\]\"");
foreach (Match match in matches)
{
//获取匹配的值
var value = match.Value;
//将匹配的值替换成"ConfigValue": "null"
dataString = dataString.Replace(value, "\"ConfigValue\": \"null\"");
}
#region 针对导出的json字符串嵌套json字符串如 "DefaultDataScope": "{\"Level\":5,\"ScopeCategory\":\"SCOPE_ALL\",\"ScopeDefineOrgIdList\":[]}"
//字符串是\"的替换成"
dataString = dataString.Replace("\\\"", "\"");
//字符串是\{替换成{
dataString = dataString.Replace("\"{", "{");
//字符串是}"的替换成}
dataString = dataString.Replace("}\"", "}");
//将json字符串转为实体,这里ExtJson会转为null,替换字符串把ExtJson值变为实体类型而实体类是string类型
var seedDataRecord2 = dataString.ToJsonEntity<SeedDataRecords<T>>();
#endregion 针对导出的json字符串嵌套json字符串如 "DefaultDataScope": "{\"Level\":5,\"ScopeCategory\":\"SCOPE_ALL\",\"ScopeDefineOrgIdList\":[]}"
//遍历seedDataRecord2
for (var i = 0; i < seedDataRecord2.Records.Count; i++)
{
#region 处理ExtJosn
//获取ExtJson属性
var propertyExtJson = typeof(T).GetProperty(nameof(PrimaryKeyEntity.ExtJson));
if (propertyExtJson != null)
{
//获取ExtJson的值
var extJson = propertyExtJson.GetValue(seedDataRecord2.Records[i])?.ToString();
// 如果ExtJson不为空并且包含NullableDictionary表示序列化失败了
if (!string.IsNullOrEmpty(extJson) && extJson.Contains("NullableDictionary"))
{
//设置ExtJson为seedDataRecord1对应的值
extJson = propertyExtJson.GetValue(seedDataRecord1.Records[i])?.ToString();
//seedDataRecord2赋值seedDataRecord1的ExtJson
propertyExtJson.SetValue(seedDataRecord2.Records[i], extJson);
}
}
#endregion 处理ExtJosn
#region 处理ConfigValue
//获取ExtJson属性
var propertyConfigValue = typeof(T).GetProperty("ConfigValue");
if (propertyConfigValue != null)
{
//获取configValue的值
var configValue = propertyConfigValue.GetValue(seedDataRecord2.Records[i])?.ToString();
// 如果configValue不为空并且包含NullableDictionary表示序列化失败了
if (!string.IsNullOrEmpty(configValue) && (configValue.Contains("NullableDictionary") || configValue == "null"))
{
//设置ExtJson为seedDataRecord1对应的值
configValue = propertyConfigValue.GetValue(seedDataRecord1.Records[i])?.ToString();
//seedDataRecord2赋值seedDataRecord1的ExtJson
propertyConfigValue.SetValue(seedDataRecord2.Records[i], configValue);
}
}
#endregion 处理ConfigValue
}
//种子数据赋值
seedData = seedDataRecord2.Records;
}
return seedData;
}
}
/// <summary>
/// 种子数据格式实体类,遵循Navicat导出json格式
/// </summary>
/// <typeparam name="T"></typeparam>
public class SeedDataRecords<T>
{
/// <summary>
/// 数据
/// </summary>
public List<T> Records { get; set; }
}
+206
View File
@@ -0,0 +1,206 @@
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// SqlSugar通用功能
/// </summary>
public static class SqlSugarUtils
{
/// <summary>
/// 根据特性获取所有表信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<SqlSugarTableInfo> GetTablesByAttribute<T>()
{
var tables = new List<SqlSugarTableInfo>();//结果集
// 获取实体表
var entityTypes = App.EffectiveTypes
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false))//有SugarTable特性
.Where(u => u.IsDefined(typeof(T), false));//具有指定特性
foreach (var entityType in entityTypes)
{
var tenantAttr = entityType.GetCustomAttribute<TenantAttribute>();//获取多租户特性
var configId = tenantAttr.configId.ToString();//获取租户Id
var connection = DbContext.DB.GetConnection(tenantAttr.configId.ToString());//根据租户ID获取连接信息
var entityInfo = connection.EntityMaintenance.GetEntityInfo(entityType);//获取实体信息
if (entityInfo != null)
{
var columns = GetTableColumns(configId, entityInfo.DbTableName);//获取字段信息
tables.Add(new SqlSugarTableInfo
{
TableName = entityInfo.DbTableName,
EntityName = entityInfo.EntityName,
TableDescription = entityInfo.TableDescription,
ConfigId = configId,
Columns = columns
});
}
}
return tables;
}
/// <summary>
/// 获取字段信息
/// </summary>
/// <param name="configId"></param>
/// <param name="tableName"></param>
/// <returns></returns>
public static List<SqlSugarColumnInfo> GetTableColumns(string configId, string tableName)
{
var columns = new List<SqlSugarColumnInfo>();//结果集
var connection = DbContext.DB.GetConnection(configId);
var dbColumnInfos = connection.DbMaintenance.GetColumnInfosByTableName(tableName);//根据表名获取表信息
if (dbColumnInfos != null)
{
//遍历字段获取信息
dbColumnInfos.ForEach(it =>
{
if (it.DbColumnName.Contains("_"))//如果有下划线,转换一下
{
var column = "";//新的字段值
var columnList = it.DbColumnName.Split('_');//根据下划线分割
columnList.ForEach(it =>
{
column += StringHelper.FirstCharToUpper(it);//首字母大写
});
it.DbColumnName = column;//赋值给数据库字段
}
else
{
it.DbColumnName = StringHelper.FirstCharToUpper(it.DbColumnName);//首字母大写
}
columns.Add(new SqlSugarColumnInfo
{
ColumnName = it.DbColumnName,
IsPrimaryKey = it.IsPrimarykey,
ColumnDescription = it.ColumnDescription,
DataType = it.DataType
});
});
}
return columns;
}
/// <summary>
/// 数据库字段类型转.NET类型
/// </summary>
/// <param name="dataType">字段类型</param>
/// <returns></returns>
public static string ConvertDataType(string dataType)
{
switch (dataType)
{
case "text":
case "varchar":
case "char":
case "nvarchar":
case "nchar":
case "blob":
case "longtext":
case "nclob":
return "string";
case "int":
case "mediumint":
return "int";
case "smallint":
return "Int16";
case "tinyint":
return "byte";
case "bigint":
case "integer"://sqlite数据库
return "long";
case "bit":
case "boolean":
return "bool";
case "money":
case "smallmoney":
case "numeric":
case "decimal":
return "decimal";
case "real":
return "Single";
case "datetime":
case "smalldatetime":
case "timestamp":
case "date":
case "year":
case "time":
return "DateTime";
case "float":
case "double":
return "double";
case "image":
case "binary":
case "varbinary":
return "byte[]";
case "uniqueidentifier":
return "Guid";
default:
return "string";
}
}
/// <summary>
/// 数据类型转显示类型
/// </summary>
/// <param name="dataType"></param>
/// <returns></returns>
public static string DataTypeToEff(string dataType)
{
return dataType switch
{
"string" => EffTypeConst.INPUT,
"int" => EffTypeConst.INPUT_NUMBER,
"long" => EffTypeConst.INPUT,
"float" => EffTypeConst.INPUT,
"double" => EffTypeConst.INPUT,
"decimal" => EffTypeConst.INPUT,
"bool" => EffTypeConst.SWITCH,
"Guid" => EffTypeConst.INPUT,
"DateTime" => EffTypeConst.DATEPICKER,
_ => EffTypeConst.INPUT
};
}
/// <summary>
/// 是否通用字段
/// </summary>
/// <param name="columnName">字段名</param>
/// <returns></returns>
public static bool IsCommonColumn(string columnName)
{
var columnList = new List<string>
{
nameof(BaseEntity.CreateTime), nameof(BaseEntity.UpdateTime),
nameof(BaseEntity.CreateUserId), nameof(BaseEntity.CreateUser),
nameof(BaseEntity.UpdateUserId), nameof(BaseEntity.UpdateUser),
nameof(BaseEntity.IsDelete), nameof(DataEntityBase.CreateOrgId),
nameof(PrimaryKeyEntity.ExtJson)
};
return columnList.Contains(columnName);
}
}