diff --git a/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableEntityEngine.cs b/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableEntityEngine.cs index b5f2e083..49f3aad3 100644 --- a/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableEntityEngine.cs +++ b/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableEntityEngine.cs @@ -11,12 +11,12 @@ public class TableEntityEngine : ViewEngineModel /// /// 作者 /// - public string AuthorName { get; set; } = "喵你个汪呀"; + public string AuthorName { get; set; } = "Admin.NET"; /// /// 邮箱 /// - public string Email { get; set; } = "jason-dom@qq.com"; + public string Email { get; set; } = "Admin.NET@qq.com"; /// /// 命名空间 diff --git a/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableSeedDataEngine.cs b/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableSeedDataEngine.cs index 662edc2b..9bb8b43d 100644 --- a/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableSeedDataEngine.cs +++ b/Admin.NET/Admin.NET.Core/CodeGen/Engines/TableSeedDataEngine.cs @@ -11,12 +11,12 @@ public class TableSeedDataEngine : ViewEngineModel /// /// 作者 /// - public string AuthorName { get; set; } = "喵你个汪呀"; + public string AuthorName { get; set; } = "Admin.NET"; /// /// 邮箱 /// - public string Email { get; set; } = "jason-dom@qq.com"; + public string Email { get; set; } = "Admin.NET@qq.com"; /// /// 命名空间 diff --git a/Admin.NET/Admin.NET.Core/CodeGen/Strategies/Entity/TableEntityCodeGenStrategy.cs b/Admin.NET/Admin.NET.Core/CodeGen/Strategies/Entity/TableEntityCodeGenStrategy.cs index 84053397..9eb7fd79 100644 --- a/Admin.NET/Admin.NET.Core/CodeGen/Strategies/Entity/TableEntityCodeGenStrategy.cs +++ b/Admin.NET/Admin.NET.Core/CodeGen/Strategies/Entity/TableEntityCodeGenStrategy.cs @@ -52,7 +52,6 @@ public class TableEntityCodeGenStrategy : CodeGenEntityStrategyBase _viewEngine.RunCompile(context, engine, builder)); result.OutPath = Path.Combine(Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent!.FullName, input.Position), result.OutPath.Replace("{ModuleName}", input.EntityName)); return [result]; diff --git a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs index 7f647efb..a85ead2c 100644 --- a/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs +++ b/Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs @@ -1,867 +1,868 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using Npgsql; - -namespace Admin.NET.Core.Service; - -/// -/// 系统数据库管理服务 🧩 -/// -[ApiDescriptionSettings(Order = 250, Description = "数据库管理")] -public class SysDatabaseService : IDynamicApiController, ITransient -{ - private readonly UserManager _userManager; - private readonly ISqlSugarClient _db; - private readonly IViewEngine _viewEngine; - private readonly CodeGenOptions _codeGenOptions; - - public SysDatabaseService(UserManager userManager, - ISqlSugarClient db, - IViewEngine viewEngine, - IOptions codeGenOptions) - { - _userManager = userManager; - _db = db; - _viewEngine = viewEngine; - _codeGenOptions = codeGenOptions.Value; - } - - /// - /// 获取库列表 🔖 - /// - /// - [DisplayName("获取库列表")] - public List GetList() - { - var dbOutputs = new List(); - var configIds = App.GetOptions().ConnectionConfigs.Select(u => u.ConfigId.ToString()).ToList(); - foreach (var config in configIds) - { - var db = _db.AsTenant().GetConnectionScope(config); - dbOutputs.Add(new DbOutput - { - ConfigId = config, - DbName = db.Ado.Connection.Database - }); - } - return dbOutputs; - } - - /// - /// 获取可视化库表结构 🔖 - /// - /// - [DisplayName("获取可视化库表结构")] - public VisualDbTable GetVisualDbTable() - { - var visualTableList = new List(); - var visualColumnList = new List(); - var columnRelationList = new List(); - var dbOptions = App.GetOptions().ConnectionConfigs.First(u => u.ConfigId.ToString() == SqlSugarConst.MainConfigId); - - // 遍历所有实体获取所有库表结构 - var random = new Random(); - var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false)).ToList(); - foreach (var entityType in entityTypes) - { - var entityInfo = _db.EntityMaintenance.GetEntityInfoNoCache(entityType); - - var visualTable = new VisualTable - { - TableName = entityInfo.DbTableName, - TableComents = entityInfo.TableDescription + entityInfo.DbTableName, - X = random.Next(5000), - Y = random.Next(5000) - }; - visualTableList.Add(visualTable); - - foreach (EntityColumnInfo columnInfo in entityInfo.Columns) - { - var visualColumn = new VisualColumn - { - TableName = columnInfo.DbTableName, - ColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName, - DataType = columnInfo.PropertyInfo.PropertyType.Name, - DataLength = columnInfo.Length.ToString(), - ColumnDescription = columnInfo.ColumnDescription, - }; - visualColumnList.Add(visualColumn); - - // 根据导航配置获取表之间关联关系 - if (columnInfo.Navigat != null) - { - var name1 = columnInfo.Navigat.GetName(); - var name2 = columnInfo.Navigat.GetName2(); - var targetColumnName = string.IsNullOrEmpty(name2) ? "Id" : name2; - var relation = new ColumnRelation - { - SourceTableName = columnInfo.DbTableName, - SourceColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(name1) : name1, - Type = columnInfo.Navigat.GetNavigateType() == NavigateType.OneToOne ? "ONE_TO_ONE" : "ONE_TO_MANY", - TargetTableName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName, - TargetColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(targetColumnName) : targetColumnName - }; - columnRelationList.Add(relation); - } - } - } - - return new VisualDbTable { VisualTableList = visualTableList, VisualColumnList = visualColumnList, ColumnRelationList = columnRelationList }; - } - - /// - /// 获取字段列表 🔖 - /// - /// 表名 - /// ConfigId - /// - [DisplayName("获取字段列表")] - public List GetColumnList(string tableName, string configId = SqlSugarConst.MainConfigId) - { - if (string.IsNullOrWhiteSpace(tableName)) return []; - - var db = _db.AsTenant().GetConnectionScope(configId); - return db.DbMaintenance.GetColumnInfosByTableName(tableName, false).Adapt>(); - } - - /// - /// 获取数据库数据类型列表 🔖 - /// - /// - /// - [DisplayName("获取数据库数据类型列表")] - public List GetDbTypeList(string configId = SqlSugarConst.MainConfigId) - { - var db = _db.AsTenant().GetConnectionScope(configId); - return db.DbMaintenance.GetDbTypes().OrderBy(u => u).ToList(); - } - - /// - /// 增加列 🔖 - /// - /// - [ApiDescriptionSettings(Name = "AddColumn"), HttpPost] - [DisplayName("增加列")] - public void AddColumn(DbColumnInput input) - { - var column = new DbColumnInfo - { - ColumnDescription = input.ColumnDescription, - DbColumnName = input.DbColumnName, - IsIdentity = input.IsIdentity == 1, - IsNullable = input.IsNullable == 1, - IsPrimarykey = input.IsPrimarykey == 1, - Length = input.Length, - DecimalDigits = input.DecimalDigits, - DataType = input.DataType - }; - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - db.DbMaintenance.AddColumn(input.TableName, column); - // 添加默认值 - if (!string.IsNullOrWhiteSpace(input.DefaultValue)) - db.DbMaintenance.AddDefaultValue(input.TableName, column.DbColumnName, input.DefaultValue); - db.DbMaintenance.AddColumnRemark(input.DbColumnName, input.TableName, input.ColumnDescription); - if (column.IsPrimarykey) - db.DbMaintenance.AddPrimaryKey(input.TableName, input.DbColumnName); - } - - /// - /// 删除列 🔖 - /// - /// - [ApiDescriptionSettings(Name = "DeleteColumn"), HttpPost] - [DisplayName("删除列")] - public void DeleteColumn(DeleteDbColumnInput input) - { - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - db.DbMaintenance.DropColumn(input.TableName, input.DbColumnName); - } - - /// - /// 编辑列 🔖 - /// - /// - [ApiDescriptionSettings(Name = "UpdateColumn"), HttpPost] - [DisplayName("编辑列")] - public void UpdateColumn(UpdateDbColumnInput input) - { - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - db.DbMaintenance.RenameColumn(input.TableName, input.OldColumnName, input.ColumnName); - if (!string.IsNullOrWhiteSpace(input.DefaultValue)) - db.DbMaintenance.AddDefaultValue(input.TableName, input.ColumnName, input.DefaultValue); - if (db.DbMaintenance.IsAnyColumnRemark(input.ColumnName, input.TableName)) - db.DbMaintenance.DeleteColumnRemark(input.ColumnName, input.TableName); - db.DbMaintenance.AddColumnRemark(input.ColumnName, input.TableName, string.IsNullOrWhiteSpace(input.Description) ? input.ColumnName : input.Description); - } - - /// - /// 移动列顺序 🔖 - /// - /// - [DisplayName("移动列顺序")] - public void MoveColumn(MoveDbColumnInput input) - { - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - var dbMaintenance = db.DbMaintenance; - - var columns = dbMaintenance.GetColumnInfosByTableName(input.TableName, false); - var targetColumn = columns.FirstOrDefault(u => u.DbColumnName.Equals(input.ColumnName, StringComparison.OrdinalIgnoreCase)) ?? throw new Exception($"列 {input.ColumnName} 在表 {input.TableName} 中不存在"); - - var dbType = db.CurrentConnectionConfig.DbType; - switch (dbType) - { - case SqlSugar.DbType.MySql: - MoveColumnInMySQL(db, input.TableName, input.ColumnName, input.AfterColumnName); - break; - - default: - throw new NotSupportedException($"暂不支持 {dbType} 数据库的列移动操作"); - } - } - - /// - /// 获取列定义 - /// - /// - /// - /// - /// - /// - /// - private string GetColumnDefinitionInMySQL(ISqlSugarClient db, string tableName, string columnName, bool noDefault = false) - { - var columnDef = db.Ado.SqlQuery($"SHOW FULL COLUMNS FROM `{tableName}` WHERE Field = '{columnName}'").FirstOrDefault() ?? throw new Exception($"Column {columnName} not found"); - - var definition = new StringBuilder(); - definition.Append($"`{columnName}` "); // 列名 - definition.Append($"{columnDef.Type} "); // 数据类型 - - // 处理约束条件 - definition.Append(columnDef.Null == "YES" ? "NULL " : "NOT NULL "); - if (columnDef.Default != null && !noDefault) - definition.Append($"DEFAULT '{columnDef.Default}' "); - if (!string.IsNullOrEmpty(columnDef.Extra)) - definition.Append($"{columnDef.Extra} "); - if (!string.IsNullOrEmpty(columnDef.Comment)) - definition.Append($"COMMENT '{columnDef.Comment.Replace("'", "''")}'"); - - return definition.ToString(); - } - - /// - /// MySQL 列移动实现 - /// - /// - /// - /// - /// - private void MoveColumnInMySQL(ISqlSugarClient db, string tableName, string columnName, string afterColumnName) - { - var definition = GetColumnDefinitionInMySQL(db, tableName, columnName); - var sql = new StringBuilder(); - sql.Append($"ALTER TABLE `{tableName}` MODIFY COLUMN {definition}"); - - if (string.IsNullOrEmpty(afterColumnName)) - sql.Append(" FIRST"); - else - sql.Append($" AFTER `{afterColumnName}`"); - - db.Ado.ExecuteCommand(sql.ToString()); - } - - /// - /// 获取表列表 🔖 - /// - /// ConfigId - /// - [DisplayName("获取表列表")] - public List GetTableList(string configId = SqlSugarConst.MainConfigId) - { - var db = _db.AsTenant().GetConnectionScope(configId); - return db.DbMaintenance.GetTableInfoList(false); - } - - /// - /// 增加表 🔖 - /// - /// - [ApiDescriptionSettings(Name = "AddTable"), HttpPost] - [DisplayName("增加表")] - public void AddTable(DbTableInput input) - { - if (input.DbColumnInfoList == null || input.DbColumnInfoList.Count == 0) - throw Oops.Oh(ErrorCodeEnum.db1000); - - if (input.DbColumnInfoList.GroupBy(u => u.DbColumnName).Any(u => u.Count() > 1)) - throw Oops.Oh(ErrorCodeEnum.db1002); - - var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - var typeBuilder = db.DynamicBuilder().CreateClass(input.TableName, new SugarTable() { TableName = input.TableName, TableDescription = input.Description }); - input.DbColumnInfoList.ForEach(u => - { - var dbColumnName = config!.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(u.DbColumnName.Trim()) : u.DbColumnName.Trim(); - // 虚拟类都默认string类型,具体以列数据类型为准 - typeBuilder.CreateProperty(dbColumnName, typeof(string), new SugarColumn() - { - IsPrimaryKey = u.IsPrimarykey == 1, - IsIdentity = u.IsIdentity == 1, - ColumnDataType = u.DataType, - Length = u.Length, - IsNullable = u.IsNullable == 1, - DecimalDigits = u.DecimalDigits, - ColumnDescription = u.ColumnDescription, - DefaultValue = u.DefaultValue, - }); - }); - db.CodeFirst.InitTables(typeBuilder.BuilderType()); - } - - /// - /// 删除表 🔖 - /// - /// - [ApiDescriptionSettings(Name = "DeleteTable"), HttpPost] - [DisplayName("删除表")] - public void DeleteTable(DeleteDbTableInput input) - { - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - db.DbMaintenance.DropTable(input.TableName); - } - - /// - /// 编辑表 🔖 - /// - /// - [ApiDescriptionSettings(Name = "UpdateTable"), HttpPost] - [DisplayName("编辑表")] - public void UpdateTable(UpdateDbTableInput input) - { - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - db.DbMaintenance.RenameTable(input.OldTableName, input.TableName); - try - { - if (db.DbMaintenance.IsAnyTableRemark(input.TableName)) - db.DbMaintenance.DeleteTableRemark(input.TableName); - - if (!string.IsNullOrWhiteSpace(input.Description)) - db.DbMaintenance.AddTableRemark(input.TableName, input.Description); - } - catch (NotSupportedException ex) - { - throw Oops.Oh(ex.ToString()); - } - } - - /// - /// 创建实体 🔖 - /// - /// - [ApiDescriptionSettings(Name = "CreateEntity"), HttpPost] - [DisplayName("创建实体")] - public void CreateEntity(CreateEntityInput input) - { - var tResult = GenerateEntity(input); - var targetPath = GetEntityTargetPath(input); - File.WriteAllText(targetPath, tResult, Encoding.UTF8); - } - - /// - /// 创建实体文件内容 - /// - /// - /// - /// - /// - /// - [DisplayName("创建实体文件内容")] - public string GenerateEntity(string ConfigId, string TableName, string Position, string BaseClassName) - { - var input = new CreateEntityInput - { - TableName = TableName, - EntityName = TableName.ToFirstLetterUpperCase(), - ConfigId = ConfigId, - Position = string.IsNullOrWhiteSpace(Position) ? "Admin.NET.Application" : Position, - BaseClassName = string.IsNullOrWhiteSpace(BaseClassName) ? "EntityBaseId" : BaseClassName - }; - return GenerateEntity(input); - } - - /// - /// 创建实体文件内容 - /// - /// - /// - [DisplayName("创建实体文件内容")] - public string GenerateEntity(CreateEntityInput input) - { - var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); - input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Application" : input.Position; - input.EntityName = string.IsNullOrWhiteSpace(input.EntityName) - ? (config.DbSettings.EnableUnderLine ? CodeGenHelper.CamelColumnName(input.TableName, null) : input.TableName) - : input.EntityName; - string[] dbColumnNames = []; - // Entity.cs.vm中是允许创建没有基类的实体的,所以这里也要做出相同的判断 - if (!string.IsNullOrWhiteSpace(input.BaseClassName)) - { - _codeGenOptions.EntityBaseColumn.TryGetValue(input.BaseClassName, out dbColumnNames); - if (dbColumnNames is null || dbColumnNames is { Length: 0 }) - throw Oops.Oh("基类配置文件不存在此类型"); - } - - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - var dbColumnInfos = db.DbMaintenance.GetColumnInfosByTableName(input.TableName, false); - dbColumnInfos.ForEach(u => - { - // 禁止字段全是大写的 - if (u.DbColumnName.ToUpper() == u.DbColumnName) - throw new Exception($"字段命名规范错误:{u.DbColumnName} 字段全是大写字母,请用大驼峰式命名规范!"); - - u.PropertyName = config.DbSettings.EnableUnderLine ? CodeGenHelper.CamelColumnName(u.DbColumnName, dbColumnNames) : u.DbColumnName; // 转下划线后的列名需要再转回来 - u.DataType = CodeGenHelper.ConvertDataType(u, config.DbType); - }); - if (_codeGenOptions.BaseEntityNames.Contains(input.BaseClassName, StringComparer.OrdinalIgnoreCase)) - dbColumnInfos = dbColumnInfos.Where(u => !dbColumnNames.Contains(u.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList(); - - var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ?? throw Oops.Oh(ErrorCodeEnum.db1001); - var templatePath = GetEntityTemplatePath(); - var tContent = File.ReadAllText(templatePath); - var tResult = _viewEngine.RunCompileFromCached(tContent, new - { - NameSpace = $"{input.Position}.Entity", - input.TableName, - input.EntityName, - BaseClassName = string.IsNullOrWhiteSpace(input.BaseClassName) ? "" : $": {input.BaseClassName}", - input.ConfigId, - Description = string.IsNullOrWhiteSpace(dbTableInfo.Description) ? input.EntityName + "业务表" : dbTableInfo.Description, - TableField = dbColumnInfos - }); - - return tResult; - } - - /// - /// 创建种子数据 🔖 - /// - /// - [ApiDescriptionSettings(Name = "CreateSeedData"), HttpPost] - [DisplayName("创建种子数据")] - public async Task CreateSeedData(CreateSeedDataInput input) - { - var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); - input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Core" : input.Position; - - var templatePath = GetSeedDataTemplatePath(); - var db = _db.AsTenant().GetConnectionScope(input.ConfigId); - var tableInfo = db.DbMaintenance.GetTableInfoList(false).First(u => u.Name == input.TableName); // 表名 - List dbColumnInfos = db.DbMaintenance.GetColumnInfosByTableName(input.TableName, false); // 所有字段 - IEnumerable entityInfos = await GetEntityInfos(); - Type entityType = null; - foreach (var item in entityInfos) - { - if (tableInfo.Name.ToLower() != (config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(item.DbTableName) : item.DbTableName).ToLower()) continue; - entityType = item.Type; - break; - } - - if (entityType == null) throw Oops.Oh(ErrorCodeEnum.db1003); - - input.EntityName = entityType.Name; - input.SeedDataName = entityType.Name + "SeedData"; - if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix; - - // 查询所有数据 - var query = db.QueryableByObject(entityType); - // 如果 entityType.Name=="SysDictType"或"SysDictData"或"SysDictDataTenant" 加入查询条件 u.IsEnum!=1 - if (entityType.Name == "SysDictType" || entityType.Name == "SysDictData" || entityType.Name == "SysDictDataTenant") - { - if (config.DbSettings.EnableUnderLine) - query = query.Where("is_enum != 1"); - else - query = query.Where("IsEnum != 1"); - } - - // 优先用创建时间排序 - DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime"); - if (orderField != null) query = query.OrderBy(orderField.DbColumnName); - // 优先用创建时间排序,再使用第一个主键排序 - if (dbColumnInfos.Any(u => u.IsPrimarykey)) - query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName); - var records = ((IEnumerable)await query.ToListAsync()).ToDynamicList(); - - // 过滤已存在的数据 - if (input.FilterExistingData && records.Count != 0) - { - // 获取实体类型-所有种数据数据类型 - var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName)) - .Where(u => !u.GetCustomAttributes().Any()).ToList(); - if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤 - { - // 获取实体的主键对应的属性名称 - var pkInfo = entityTypes[0].GetProperties().FirstOrDefault(u => u.GetCustomAttribute()?.IsPrimaryKey == true); - if (pkInfo != null) - { - var seedDataTypes = SeedDataHelper.GetSeedDataTypeList(entityTypes[0]); - // 可能会重名的种子数据不作为过滤项 - string doNotFilterFullName1 = $"{input.Position}.SeedData.{input.SeedDataName}"; - string doNotFilterFullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData - - PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id"); - - for (int i = seedDataTypes.Count - 1; i >= 0; i--) - { - string fullName = seedDataTypes[i].FullName; - if ((fullName == doNotFilterFullName1) || (fullName == doNotFilterFullName2)) continue; - - // 删除重复数据 - var instance = Activator.CreateInstance(seedDataTypes[i]); - var hasDataMethod = seedDataTypes[i].GetMethod("HasData"); - var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); - if (seedData == null) continue; - - List recordsToRemove = []; - foreach (var record in records) - { - object recordId = pkInfo.GetValue(record); - if (seedData.Select(d1 => idPropertySeedData.GetValue(d1)).Any(dataId => recordId != null && dataId != null && recordId.Equals(dataId))) - { - recordsToRemove.Add(record); - } - } - - foreach (var itemToRemove in recordsToRemove) - { - records.Remove(itemToRemove); - } - } - } - } - } - - // 检查有没有 System.Text.Json.Serialization.JsonIgnore 或 Newtonsoft.Json.JsonIgnore 的属性 - // 如果 JsonIgnore 和 SugarColumn 都存在,那么后成序更化时就生成这了这些字段,就需要在这里另外补充(以处理用户表SysUser中的Password为例) - var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute() != null || - p.GetAttribute() != null) && p.GetAttribute() != null).ToList(); - var jsonIgnoreInfo = new List>(); - if (jsonIgnoreProperties.Count > 0) - { - int recordIndex = 0; - foreach (var r in (IEnumerable)records) - { - List record = []; - foreach (var item in jsonIgnoreProperties) - { - object v = item.GetValue(r); - string strValue = "null"; - if (v != null) - { - strValue = v.ToString(); - if (v.GetType() == typeof(string)) - strValue = "\"" + strValue + "\""; - else if (v.GetType() == typeof(DateTime)) - strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")"; - } - - record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue }); - } - - recordIndex++; - jsonIgnoreInfo.Add(record); - } - } - - // 获取所有字段信息 - var propertyList = entityType.GetProperties().Where(x => false == (x.GetCustomAttribute()?.IsIgnore ?? false)).ToList(); - for (var i = 0; i < propertyList.Count; i++) - { - if (propertyList[i].Name != nameof(EntityBaseId.Id) || !(propertyList[i].GetCustomAttribute()?.IsPrimaryKey ?? true)) continue; - var temp = propertyList[i]; - for (var j = i; j > 0; j--) propertyList[j] = propertyList[j - 1]; - propertyList[0] = temp; - } - // var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; - // var recordList = JsonConvert.SerializeObject(records, Formatting.Indented, timeConverter); - - // 拼接种子数据 - var recordList = records.Select(obj => string.Join(", ", propertyList.Select(prop => - { - var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; - object value = prop.GetValue(obj); - if (value == null) value = "null"; - else if (propType == typeof(string)) - { - value = $"@\"{value.ToString().Replace("\"", "\"\"")}\""; - } - else if (propType.IsEnum) - { - value = $"{propType.Name}.{value}"; - } - else if (propType == typeof(bool)) - { - value = (bool)value ? "true" : "false"; - } - else if (propType == typeof(DateTime)) - { - value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")"; - } - - return $"{prop.Name}={value}"; - }))).ToList(); - - var tContent = await File.ReadAllTextAsync(templatePath); - var data = new - { - NameSpace = $"{input.Position}.SeedData", - EntityNameSpace = entityType.Namespace, - input.TableName, - input.EntityName, - input.SeedDataName, - input.ConfigId, - tableInfo.Description, - JsonIgnoreInfo = jsonIgnoreInfo, - RecordList = recordList - }; - var tResult = await _viewEngine.RunCompileAsync(tContent, data, builderAction: builder => - { - builder.AddAssemblyReferenceByName("System.Linq"); - builder.AddAssemblyReferenceByName("System.Collections"); - builder.AddAssemblyReferenceByName("System.Text.RegularExpressions"); - builder.AddUsing("System.Text.RegularExpressions"); - builder.AddUsing("System.Collections.Generic"); - builder.AddUsing("System.Linq"); - }); - - var targetPath = GetSeedDataTargetPath(input); - await File.WriteAllTextAsync(targetPath, tResult, Encoding.UTF8); - } - - /// - /// 获取种子数据列表 🔖 - /// - /// - /// - [DisplayName("获取种子数据列表")] - public List GetSeedDataList([FromQuery] string configId) - { - var seedDataTypes = SeedDataHelper.GetInitSeedDataTypeList(); - var outputList = new List(); - foreach (var seedDataType in seedDataTypes) - { - var instance = Activator.CreateInstance(seedDataType); - var hasDataMethod = seedDataType.GetMethod("HasData"); - var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast().ToArray() ?? []; - - var entityType = seedDataType.GetInterfaces().First().GetGenericArguments().First(); - var seedDataAtt = seedDataType.GetCustomAttribute(); - outputList.Add(new DataInitItemOutput() - { - Name = seedDataType.Name, - AssemblyName = seedDataType.Assembly.ManifestModule.Name, - Order = seedDataAtt != null ? seedDataAtt.Order : 0, - Count = seedData.Length, - Description = entityType.GetCustomAttribute().TableDescription + "种子数据" - }); - } - return outputList; - } - - /// - /// 初始化表结构 🔖 - /// - /// - [DisplayName("初始化表结构")] - public void InitTable(InitTableInput input) - { - if (!_userManager.SuperAdmin) - throw Oops.Oh("只有超管才可以操作!"); - - var dbProvider = _db.AsTenant().GetConnectionScope(input.ConfigId); - SqlSugarSetup.InitTable(dbProvider, false, input.EntityNames); - } - - /// - /// 初始化种子数据 🔖 - /// - /// - [DisplayName("初始化种子数据")] - public void InitSeedData(InitSeedDataInput input) - { - if (!_userManager.SuperAdmin) - throw Oops.Oh("只有超管才可以操作!"); - - var dbProvider = _db.AsTenant().GetConnectionScope(input.ConfigId); - SqlSugarSetup.InitSeedData(dbProvider, false, input.SeedNames); - } - - /// - /// 获取库表信息 - /// - /// - private async Task> GetEntityInfos() - { - var entityInfos = new List(); - - var type = typeof(SugarTable); - var types = new List(); - if (_codeGenOptions.EntityAssemblyNames != null) - { - foreach (var asm in _codeGenOptions.EntityAssemblyNames.Select(Assembly.Load)) - { - types.AddRange(asm.GetExportedTypes().ToList()); - } - } - - Type[] cosType = types.Where(u => IsMyAttribute(Attribute.GetCustomAttributes(u, true))).ToArray(); - foreach (var c in cosType) - { - var sugarAttribute = c.GetCustomAttributes(type, true)?.FirstOrDefault(); - - var des = c.GetCustomAttributes(typeof(DescriptionAttribute), true); - var description = ""; - if (des.Length > 0) - { - description = ((DescriptionAttribute)des[0]).Description; - } - - entityInfos.Add(new EntityInfo() - { - EntityName = c.Name, - DbTableName = sugarAttribute == null ? c.Name : ((SugarTable)sugarAttribute).TableName, - TableDescription = description, - Type = c - }); - } - - return await Task.FromResult(entityInfos); - - bool IsMyAttribute(Attribute[] o) - { - return o.Any(a => a.GetType() == type); - } - } - - /// - /// 获取实体模板文件路径 - /// - /// - private static string GetEntityTemplatePath() - { - var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template"); - return Path.Combine(templatePath, "Entity.cs.vm"); - } - - /// - /// 获取种子数据模板文件路径 - /// - /// - private static string GetSeedDataTemplatePath() - { - var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template"); - return Path.Combine(templatePath, "SeedData.cs.vm"); - } - - /// - /// 设置生成实体文件路径 - /// - /// - /// - private static string GetEntityTargetPath(CreateEntityInput input) - { - var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, input.Position, "Entity"); - if (!Directory.Exists(backendPath)) - Directory.CreateDirectory(backendPath); - return Path.Combine(backendPath, input.EntityName + ".cs"); - } - - /// - /// 设置生成种子数据文件路径 - /// - /// - /// - private static string GetSeedDataTargetPath(CreateSeedDataInput input) - { - var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, input.Position, "SeedData"); - if (!Directory.Exists(backendPath)) - Directory.CreateDirectory(backendPath); - return Path.Combine(backendPath, input.SeedDataName + ".cs"); - } - - /// - /// 备份数据库(PostgreSQL)🔖 - /// - /// - [HttpPost, NonUnify] - [DisplayName("备份数据库(PostgreSQL)")] - public async Task BackupDatabase() - { - if (_db.CurrentConnectionConfig.DbType != SqlSugar.DbType.PostgreSQL) - throw Oops.Oh("只支持 PostgreSQL 数据库 😁"); - - var npgsqlConn = new NpgsqlConnectionStringBuilder(_db.CurrentConnectionConfig.ConnectionString); - if (npgsqlConn == null || string.IsNullOrWhiteSpace(npgsqlConn.Host) || string.IsNullOrWhiteSpace(npgsqlConn.Username) || string.IsNullOrWhiteSpace(npgsqlConn.Password) || - string.IsNullOrWhiteSpace(npgsqlConn.Database)) - throw Oops.Oh("PostgreSQL 数据库配置错误"); - - // 确保备份目录存在 - var backupDirectory = Path.Combine(Directory.GetCurrentDirectory(), "backups"); - Directory.CreateDirectory(backupDirectory); - - // 构建备份文件名 - string backupFileName = $"backup_{DateTime.Now:yyyyMMddHHmmss}.sql"; - string backupFilePath = Path.Combine(backupDirectory, backupFileName); - - // 启动pg_dump进程进行备份 - // 设置密码:export PGPASSWORD='xxxxxx' - var bash = $"-U {npgsqlConn.Username} -h {npgsqlConn.Host} -p {npgsqlConn.Port} -E UTF8 -F c -b -v -f {backupFilePath} {npgsqlConn.Database}"; - var startInfo = new ProcessStartInfo - { - FileName = "pg_dump", - Arguments = bash, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - EnvironmentVariables = - { - ["PGPASSWORD"] = npgsqlConn.Password - } - }; - - //_logger.LogInformation("备份数据库:pg_dump " + bash); - - //try - //{ - using (var backupProcess = Process.Start(startInfo)) - { - await backupProcess.WaitForExitAsync(); - - //var output = await backupProcess.StandardOutput.ReadToEndAsync(); - //var error = await backupProcess.StandardError.ReadToEndAsync(); - - // 检查备份是否成功 - if (backupProcess.ExitCode != 0) - { - throw Oops.Oh($"备份失败:ExitCode({backupProcess.ExitCode})"); - } - } - - // _logger.LogInformation($"备份成功:{backupFilePath}"); - //} - //catch (Exception ex) - //{ - // _logger.LogError(ex, $"备份失败:"); - // throw; - //} - - // 若备份成功则提供下载链接 - return new FileStreamResult(new FileStream(backupFilePath, FileMode.Open), "application/octet-stream") - { - FileDownloadName = backupFileName - }; - } +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using Npgsql; + +namespace Admin.NET.Core.Service; + +/// +/// 系统数据库管理服务 🧩 +/// +[ApiDescriptionSettings(Order = 250, Description = "数据库管理")] +public class SysDatabaseService : IDynamicApiController, ITransient +{ + private readonly UserManager _userManager; + private readonly ISqlSugarClient _db; + private readonly IViewEngine _viewEngine; + private readonly CodeGenOptions _codeGenOptions; + + public SysDatabaseService(UserManager userManager, + ISqlSugarClient db, + IViewEngine viewEngine, + IOptions codeGenOptions) + { + _userManager = userManager; + _db = db; + _viewEngine = viewEngine; + _codeGenOptions = codeGenOptions.Value; + } + + /// + /// 获取库列表 🔖 + /// + /// + [DisplayName("获取库列表")] + public List GetList() + { + var dbOutputs = new List(); + var configIds = App.GetOptions().ConnectionConfigs.Select(u => u.ConfigId.ToString()).ToList(); + foreach (var config in configIds) + { + var db = _db.AsTenant().GetConnectionScope(config); + dbOutputs.Add(new DbOutput + { + ConfigId = config, + DbName = db.Ado.Connection.Database + }); + } + return dbOutputs; + } + + /// + /// 获取可视化库表结构 🔖 + /// + /// + [DisplayName("获取可视化库表结构")] + public VisualDbTable GetVisualDbTable() + { + var visualTableList = new List(); + var visualColumnList = new List(); + var columnRelationList = new List(); + var dbOptions = App.GetOptions().ConnectionConfigs.First(u => u.ConfigId.ToString() == SqlSugarConst.MainConfigId); + + // 遍历所有实体获取所有库表结构 + var random = new Random(); + var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false)).ToList(); + foreach (var entityType in entityTypes) + { + var entityInfo = _db.EntityMaintenance.GetEntityInfoNoCache(entityType); + + var visualTable = new VisualTable + { + TableName = entityInfo.DbTableName, + TableComents = entityInfo.TableDescription + entityInfo.DbTableName, + X = random.Next(5000), + Y = random.Next(5000) + }; + visualTableList.Add(visualTable); + + foreach (EntityColumnInfo columnInfo in entityInfo.Columns) + { + var visualColumn = new VisualColumn + { + TableName = columnInfo.DbTableName, + ColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName, + DataType = columnInfo.PropertyInfo.PropertyType.Name, + DataLength = columnInfo.Length.ToString(), + ColumnDescription = columnInfo.ColumnDescription, + }; + visualColumnList.Add(visualColumn); + + // 根据导航配置获取表之间关联关系 + if (columnInfo.Navigat != null) + { + var name1 = columnInfo.Navigat.GetName(); + var name2 = columnInfo.Navigat.GetName2(); + var targetColumnName = string.IsNullOrEmpty(name2) ? "Id" : name2; + var relation = new ColumnRelation + { + SourceTableName = columnInfo.DbTableName, + SourceColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(name1) : name1, + Type = columnInfo.Navigat.GetNavigateType() == NavigateType.OneToOne ? "ONE_TO_ONE" : "ONE_TO_MANY", + TargetTableName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(columnInfo.DbColumnName) : columnInfo.DbColumnName, + TargetColumnName = dbOptions.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(targetColumnName) : targetColumnName + }; + columnRelationList.Add(relation); + } + } + } + + return new VisualDbTable { VisualTableList = visualTableList, VisualColumnList = visualColumnList, ColumnRelationList = columnRelationList }; + } + + /// + /// 获取字段列表 🔖 + /// + /// 表名 + /// ConfigId + /// + [DisplayName("获取字段列表")] + public List GetColumnList(string tableName, string configId = SqlSugarConst.MainConfigId) + { + if (string.IsNullOrWhiteSpace(tableName)) return []; + + var db = _db.AsTenant().GetConnectionScope(configId); + return db.DbMaintenance.GetColumnInfosByTableName(tableName, false).Adapt>(); + } + + /// + /// 获取数据库数据类型列表 🔖 + /// + /// + /// + [DisplayName("获取数据库数据类型列表")] + public List GetDbTypeList(string configId = SqlSugarConst.MainConfigId) + { + var db = _db.AsTenant().GetConnectionScope(configId); + return db.DbMaintenance.GetDbTypes().OrderBy(u => u).ToList(); + } + + /// + /// 增加列 🔖 + /// + /// + [ApiDescriptionSettings(Name = "AddColumn"), HttpPost] + [DisplayName("增加列")] + public void AddColumn(DbColumnInput input) + { + var column = new DbColumnInfo + { + ColumnDescription = input.ColumnDescription, + DbColumnName = input.DbColumnName, + IsIdentity = input.IsIdentity == 1, + IsNullable = input.IsNullable == 1, + IsPrimarykey = input.IsPrimarykey == 1, + Length = input.Length, + DecimalDigits = input.DecimalDigits, + DataType = input.DataType + }; + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + db.DbMaintenance.AddColumn(input.TableName, column); + // 添加默认值 + if (!string.IsNullOrWhiteSpace(input.DefaultValue)) + db.DbMaintenance.AddDefaultValue(input.TableName, column.DbColumnName, input.DefaultValue); + db.DbMaintenance.AddColumnRemark(input.DbColumnName, input.TableName, input.ColumnDescription); + if (column.IsPrimarykey) + db.DbMaintenance.AddPrimaryKey(input.TableName, input.DbColumnName); + } + + /// + /// 删除列 🔖 + /// + /// + [ApiDescriptionSettings(Name = "DeleteColumn"), HttpPost] + [DisplayName("删除列")] + public void DeleteColumn(DeleteDbColumnInput input) + { + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + db.DbMaintenance.DropColumn(input.TableName, input.DbColumnName); + } + + /// + /// 编辑列 🔖 + /// + /// + [ApiDescriptionSettings(Name = "UpdateColumn"), HttpPost] + [DisplayName("编辑列")] + public void UpdateColumn(UpdateDbColumnInput input) + { + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + db.DbMaintenance.RenameColumn(input.TableName, input.OldColumnName, input.ColumnName); + if (!string.IsNullOrWhiteSpace(input.DefaultValue)) + db.DbMaintenance.AddDefaultValue(input.TableName, input.ColumnName, input.DefaultValue); + if (db.DbMaintenance.IsAnyColumnRemark(input.ColumnName, input.TableName)) + db.DbMaintenance.DeleteColumnRemark(input.ColumnName, input.TableName); + db.DbMaintenance.AddColumnRemark(input.ColumnName, input.TableName, string.IsNullOrWhiteSpace(input.Description) ? input.ColumnName : input.Description); + } + + /// + /// 移动列顺序 🔖 + /// + /// + [DisplayName("移动列顺序")] + public void MoveColumn(MoveDbColumnInput input) + { + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + var dbMaintenance = db.DbMaintenance; + + var columns = dbMaintenance.GetColumnInfosByTableName(input.TableName, false); + var targetColumn = columns.FirstOrDefault(u => u.DbColumnName.Equals(input.ColumnName, StringComparison.OrdinalIgnoreCase)) ?? throw new Exception($"列 {input.ColumnName} 在表 {input.TableName} 中不存在"); + + var dbType = db.CurrentConnectionConfig.DbType; + switch (dbType) + { + case SqlSugar.DbType.MySql: + MoveColumnInMySQL(db, input.TableName, input.ColumnName, input.AfterColumnName); + break; + + default: + throw new NotSupportedException($"暂不支持 {dbType} 数据库的列移动操作"); + } + } + + /// + /// 获取列定义 + /// + /// + /// + /// + /// + /// + /// + private string GetColumnDefinitionInMySQL(ISqlSugarClient db, string tableName, string columnName, bool noDefault = false) + { + var columnDef = db.Ado.SqlQuery($"SHOW FULL COLUMNS FROM `{tableName}` WHERE Field = '{columnName}'").FirstOrDefault() ?? throw new Exception($"Column {columnName} not found"); + + var definition = new StringBuilder(); + definition.Append($"`{columnName}` "); // 列名 + definition.Append($"{columnDef.Type} "); // 数据类型 + + // 处理约束条件 + definition.Append(columnDef.Null == "YES" ? "NULL " : "NOT NULL "); + if (columnDef.Default != null && !noDefault) + definition.Append($"DEFAULT '{columnDef.Default}' "); + if (!string.IsNullOrEmpty(columnDef.Extra)) + definition.Append($"{columnDef.Extra} "); + if (!string.IsNullOrEmpty(columnDef.Comment)) + definition.Append($"COMMENT '{columnDef.Comment.Replace("'", "''")}'"); + + return definition.ToString(); + } + + /// + /// MySQL 列移动实现 + /// + /// + /// + /// + /// + private void MoveColumnInMySQL(ISqlSugarClient db, string tableName, string columnName, string afterColumnName) + { + var definition = GetColumnDefinitionInMySQL(db, tableName, columnName); + var sql = new StringBuilder(); + sql.Append($"ALTER TABLE `{tableName}` MODIFY COLUMN {definition}"); + + if (string.IsNullOrEmpty(afterColumnName)) + sql.Append(" FIRST"); + else + sql.Append($" AFTER `{afterColumnName}`"); + + db.Ado.ExecuteCommand(sql.ToString()); + } + + /// + /// 获取表列表 🔖 + /// + /// ConfigId + /// + [DisplayName("获取表列表")] + public List GetTableList(string configId = SqlSugarConst.MainConfigId) + { + var db = _db.AsTenant().GetConnectionScope(configId); + return db.DbMaintenance.GetTableInfoList(false); + } + + /// + /// 增加表 🔖 + /// + /// + [ApiDescriptionSettings(Name = "AddTable"), HttpPost] + [DisplayName("增加表")] + public void AddTable(DbTableInput input) + { + if (input.DbColumnInfoList == null || input.DbColumnInfoList.Count == 0) + throw Oops.Oh(ErrorCodeEnum.db1000); + + if (input.DbColumnInfoList.GroupBy(u => u.DbColumnName).Any(u => u.Count() > 1)) + throw Oops.Oh(ErrorCodeEnum.db1002); + + var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + var typeBuilder = db.DynamicBuilder().CreateClass(input.TableName, new SugarTable() { TableName = input.TableName, TableDescription = input.Description }); + input.DbColumnInfoList.ForEach(u => + { + var dbColumnName = config!.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(u.DbColumnName.Trim()) : u.DbColumnName.Trim(); + // 虚拟类都默认string类型,具体以列数据类型为准 + typeBuilder.CreateProperty(dbColumnName, typeof(string), new SugarColumn() + { + IsPrimaryKey = u.IsPrimarykey == 1, + IsIdentity = u.IsIdentity == 1, + ColumnDataType = u.DataType, + Length = u.Length, + IsNullable = u.IsNullable == 1, + DecimalDigits = u.DecimalDigits, + ColumnDescription = u.ColumnDescription, + DefaultValue = u.DefaultValue, + }); + }); + db.CodeFirst.InitTables(typeBuilder.BuilderType()); + } + + /// + /// 删除表 🔖 + /// + /// + [ApiDescriptionSettings(Name = "DeleteTable"), HttpPost] + [DisplayName("删除表")] + public void DeleteTable(DeleteDbTableInput input) + { + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + db.DbMaintenance.DropTable(input.TableName); + } + + /// + /// 编辑表 🔖 + /// + /// + [ApiDescriptionSettings(Name = "UpdateTable"), HttpPost] + [DisplayName("编辑表")] + public void UpdateTable(UpdateDbTableInput input) + { + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + db.DbMaintenance.RenameTable(input.OldTableName, input.TableName); + try + { + if (db.DbMaintenance.IsAnyTableRemark(input.TableName)) + db.DbMaintenance.DeleteTableRemark(input.TableName); + + if (!string.IsNullOrWhiteSpace(input.Description)) + db.DbMaintenance.AddTableRemark(input.TableName, input.Description); + } + catch (NotSupportedException ex) + { + throw Oops.Oh(ex.ToString()); + } + } + + /// + /// 创建实体 🔖 + /// + /// + [ApiDescriptionSettings(Name = "CreateEntity"), HttpPost] + [DisplayName("创建实体")] + public void CreateEntity(CreateEntityInput input) + { + var tResult = GenerateEntity(input); + var targetPath = GetEntityTargetPath(input); + File.WriteAllText(targetPath, tResult, Encoding.UTF8); + } + + /// + /// 创建实体文件内容 + /// + /// + /// + /// + /// + /// + [DisplayName("创建实体文件内容")] + public string GenerateEntity(string ConfigId, string TableName, string Position, string BaseClassName) + { + var input = new CreateEntityInput + { + TableName = TableName, + EntityName = TableName.ToFirstLetterUpperCase(), + ConfigId = ConfigId, + Position = string.IsNullOrWhiteSpace(Position) ? "Admin.NET.Application" : Position, + BaseClassName = string.IsNullOrWhiteSpace(BaseClassName) ? "EntityBaseId" : BaseClassName + }; + return GenerateEntity(input); + } + + /// + /// 创建实体文件内容 + /// + /// + /// + [DisplayName("创建实体文件内容")] + public string GenerateEntity(CreateEntityInput input) + { + var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); + input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Application" : input.Position; + input.EntityName = string.IsNullOrWhiteSpace(input.EntityName) + ? (config.DbSettings.EnableUnderLine ? CodeGenHelper.CamelColumnName(input.TableName, null) : input.TableName) + : input.EntityName; + string[] dbColumnNames = []; + // Entity.cs.vm中是允许创建没有基类的实体的,所以这里也要做出相同的判断 + if (!string.IsNullOrWhiteSpace(input.BaseClassName)) + { + _codeGenOptions.EntityBaseColumn.TryGetValue(input.BaseClassName, out dbColumnNames); + if (dbColumnNames is null || dbColumnNames is { Length: 0 }) + throw Oops.Oh("基类配置文件不存在此类型"); + } + + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + var dbColumnInfos = db.DbMaintenance.GetColumnInfosByTableName(input.TableName, false); + dbColumnInfos.ForEach(u => + { + // 禁止字段全是大写的 + if (u.DbColumnName.ToUpper() == u.DbColumnName) + throw new Exception($"字段命名规范错误:{u.DbColumnName} 字段全是大写字母,请用大驼峰式命名规范!"); + + u.PropertyName = config.DbSettings.EnableUnderLine ? CodeGenHelper.CamelColumnName(u.DbColumnName, dbColumnNames) : u.DbColumnName; // 转下划线后的列名需要再转回来 + u.DataType = CodeGenHelper.ConvertDataType(u, config.DbType); + }); + if (_codeGenOptions.BaseEntityNames.Contains(input.BaseClassName, StringComparer.OrdinalIgnoreCase)) + dbColumnInfos = dbColumnInfos.Where(u => !dbColumnNames.Contains(u.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList(); + + var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ?? throw Oops.Oh(ErrorCodeEnum.db1001); + var templatePath = GetEntityTemplatePath(); + var tContent = File.ReadAllText(templatePath); + var tResult = _viewEngine.RunCompileFromCached(tContent, new + { + NameSpace = $"{input.Position}.Entity", + TableName = input.TableName, + EntityName = input.EntityName, + BaseClassName = string.IsNullOrWhiteSpace(input.BaseClassName) ? "" : $": {input.BaseClassName}", + ConfigId = input.ConfigId, + Description = string.IsNullOrWhiteSpace(dbTableInfo.Description) ? input.EntityName + "业务表" : dbTableInfo.Description, + TableFields = dbColumnInfos, + AuthorName = "Admin.NET", + Email = "Admin.NET@qq.com" + }); + return tResult; + } + + /// + /// 创建种子数据 🔖 + /// + /// + [ApiDescriptionSettings(Name = "CreateSeedData"), HttpPost] + [DisplayName("创建种子数据")] + public async Task CreateSeedData(CreateSeedDataInput input) + { + var config = App.GetOptions().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); + input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Core" : input.Position; + + var templatePath = GetSeedDataTemplatePath(); + var db = _db.AsTenant().GetConnectionScope(input.ConfigId); + var tableInfo = db.DbMaintenance.GetTableInfoList(false).First(u => u.Name == input.TableName); // 表名 + List dbColumnInfos = db.DbMaintenance.GetColumnInfosByTableName(input.TableName, false); // 所有字段 + IEnumerable entityInfos = await GetEntityInfos(); + Type entityType = null; + foreach (var item in entityInfos) + { + if (tableInfo.Name.ToLower() != (config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(item.DbTableName) : item.DbTableName).ToLower()) continue; + entityType = item.Type; + break; + } + + if (entityType == null) throw Oops.Oh(ErrorCodeEnum.db1003); + + input.EntityName = entityType.Name; + input.SeedDataName = entityType.Name + "SeedData"; + if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix; + + // 查询所有数据 + var query = db.QueryableByObject(entityType); + // 如果 entityType.Name=="SysDictType"或"SysDictData"或"SysDictDataTenant" 加入查询条件 u.IsEnum!=1 + if (entityType.Name == "SysDictType" || entityType.Name == "SysDictData" || entityType.Name == "SysDictDataTenant") + { + if (config.DbSettings.EnableUnderLine) + query = query.Where("is_enum != 1"); + else + query = query.Where("IsEnum != 1"); + } + + // 优先用创建时间排序 + DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime"); + if (orderField != null) query = query.OrderBy(orderField.DbColumnName); + // 优先用创建时间排序,再使用第一个主键排序 + if (dbColumnInfos.Any(u => u.IsPrimarykey)) + query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName); + var records = ((IEnumerable)await query.ToListAsync()).ToDynamicList(); + + // 过滤已存在的数据 + if (input.FilterExistingData && records.Count != 0) + { + // 获取实体类型-所有种数据数据类型 + var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName)) + .Where(u => !u.GetCustomAttributes().Any()).ToList(); + if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤 + { + // 获取实体的主键对应的属性名称 + var pkInfo = entityTypes[0].GetProperties().FirstOrDefault(u => u.GetCustomAttribute()?.IsPrimaryKey == true); + if (pkInfo != null) + { + var seedDataTypes = SeedDataHelper.GetSeedDataTypeList(entityTypes[0]); + // 可能会重名的种子数据不作为过滤项 + string doNotFilterFullName1 = $"{input.Position}.SeedData.{input.SeedDataName}"; + string doNotFilterFullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData + + PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id"); + + for (int i = seedDataTypes.Count - 1; i >= 0; i--) + { + string fullName = seedDataTypes[i].FullName; + if ((fullName == doNotFilterFullName1) || (fullName == doNotFilterFullName2)) continue; + + // 删除重复数据 + var instance = Activator.CreateInstance(seedDataTypes[i]); + var hasDataMethod = seedDataTypes[i].GetMethod("HasData"); + var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); + if (seedData == null) continue; + + List recordsToRemove = []; + foreach (var record in records) + { + object recordId = pkInfo.GetValue(record); + if (seedData.Select(d1 => idPropertySeedData.GetValue(d1)).Any(dataId => recordId != null && dataId != null && recordId.Equals(dataId))) + { + recordsToRemove.Add(record); + } + } + + foreach (var itemToRemove in recordsToRemove) + { + records.Remove(itemToRemove); + } + } + } + } + } + + // 检查有没有 System.Text.Json.Serialization.JsonIgnore 或 Newtonsoft.Json.JsonIgnore 的属性 + // 如果 JsonIgnore 和 SugarColumn 都存在,那么后成序更化时就生成这了这些字段,就需要在这里另外补充(以处理用户表SysUser中的Password为例) + var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute() != null || + p.GetAttribute() != null) && p.GetAttribute() != null).ToList(); + var jsonIgnoreInfo = new List>(); + if (jsonIgnoreProperties.Count > 0) + { + int recordIndex = 0; + foreach (var r in (IEnumerable)records) + { + List record = []; + foreach (var item in jsonIgnoreProperties) + { + object v = item.GetValue(r); + string strValue = "null"; + if (v != null) + { + strValue = v.ToString(); + if (v.GetType() == typeof(string)) + strValue = "\"" + strValue + "\""; + else if (v.GetType() == typeof(DateTime)) + strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")"; + } + + record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue }); + } + + recordIndex++; + jsonIgnoreInfo.Add(record); + } + } + + // 获取所有字段信息 + var propertyList = entityType.GetProperties().Where(x => false == (x.GetCustomAttribute()?.IsIgnore ?? false)).ToList(); + for (var i = 0; i < propertyList.Count; i++) + { + if (propertyList[i].Name != nameof(EntityBaseId.Id) || !(propertyList[i].GetCustomAttribute()?.IsPrimaryKey ?? true)) continue; + var temp = propertyList[i]; + for (var j = i; j > 0; j--) propertyList[j] = propertyList[j - 1]; + propertyList[0] = temp; + } + // var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; + // var recordList = JsonConvert.SerializeObject(records, Formatting.Indented, timeConverter); + + // 拼接种子数据 + var recordList = records.Select(obj => string.Join(", ", propertyList.Select(prop => + { + var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; + object value = prop.GetValue(obj); + if (value == null) value = "null"; + else if (propType == typeof(string)) + { + value = $"@\"{value.ToString().Replace("\"", "\"\"")}\""; + } + else if (propType.IsEnum) + { + value = $"{propType.Name}.{value}"; + } + else if (propType == typeof(bool)) + { + value = (bool)value ? "true" : "false"; + } + else if (propType == typeof(DateTime)) + { + value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")"; + } + + return $"{prop.Name}={value}"; + }))).ToList(); + + var tContent = await File.ReadAllTextAsync(templatePath); + var data = new + { + NameSpace = $"{input.Position}.SeedData", + EntityNameSpace = entityType.Namespace, + input.TableName, + input.EntityName, + input.SeedDataName, + input.ConfigId, + tableInfo.Description, + JsonIgnoreInfo = jsonIgnoreInfo, + RecordList = recordList + }; + var tResult = await _viewEngine.RunCompileAsync(tContent, data, builderAction: builder => + { + builder.AddAssemblyReferenceByName("System.Linq"); + builder.AddAssemblyReferenceByName("System.Collections"); + builder.AddAssemblyReferenceByName("System.Text.RegularExpressions"); + builder.AddUsing("System.Text.RegularExpressions"); + builder.AddUsing("System.Collections.Generic"); + builder.AddUsing("System.Linq"); + }); + + var targetPath = GetSeedDataTargetPath(input); + await File.WriteAllTextAsync(targetPath, tResult, Encoding.UTF8); + } + + /// + /// 获取种子数据列表 🔖 + /// + /// + /// + [DisplayName("获取种子数据列表")] + public List GetSeedDataList([FromQuery] string configId) + { + var seedDataTypes = SeedDataHelper.GetInitSeedDataTypeList(); + var outputList = new List(); + foreach (var seedDataType in seedDataTypes) + { + var instance = Activator.CreateInstance(seedDataType); + var hasDataMethod = seedDataType.GetMethod("HasData"); + var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast().ToArray() ?? []; + + var entityType = seedDataType.GetInterfaces().First().GetGenericArguments().First(); + var seedDataAtt = seedDataType.GetCustomAttribute(); + outputList.Add(new DataInitItemOutput() + { + Name = seedDataType.Name, + AssemblyName = seedDataType.Assembly.ManifestModule.Name, + Order = seedDataAtt != null ? seedDataAtt.Order : 0, + Count = seedData.Length, + Description = entityType.GetCustomAttribute().TableDescription + "种子数据" + }); + } + return outputList; + } + + /// + /// 初始化表结构 🔖 + /// + /// + [DisplayName("初始化表结构")] + public void InitTable(InitTableInput input) + { + if (!_userManager.SuperAdmin) + throw Oops.Oh("只有超管才可以操作!"); + + var dbProvider = _db.AsTenant().GetConnectionScope(input.ConfigId); + SqlSugarSetup.InitTable(dbProvider, false, input.EntityNames); + } + + /// + /// 初始化种子数据 🔖 + /// + /// + [DisplayName("初始化种子数据")] + public void InitSeedData(InitSeedDataInput input) + { + if (!_userManager.SuperAdmin) + throw Oops.Oh("只有超管才可以操作!"); + + var dbProvider = _db.AsTenant().GetConnectionScope(input.ConfigId); + SqlSugarSetup.InitSeedData(dbProvider, false, input.SeedNames); + } + + /// + /// 获取库表信息 + /// + /// + private async Task> GetEntityInfos() + { + var entityInfos = new List(); + + var type = typeof(SugarTable); + var types = new List(); + if (_codeGenOptions.EntityAssemblyNames != null) + { + foreach (var asm in _codeGenOptions.EntityAssemblyNames.Select(Assembly.Load)) + { + types.AddRange(asm.GetExportedTypes().ToList()); + } + } + + Type[] cosType = types.Where(u => IsMyAttribute(Attribute.GetCustomAttributes(u, true))).ToArray(); + foreach (var c in cosType) + { + var sugarAttribute = c.GetCustomAttributes(type, true)?.FirstOrDefault(); + + var des = c.GetCustomAttributes(typeof(DescriptionAttribute), true); + var description = ""; + if (des.Length > 0) + { + description = ((DescriptionAttribute)des[0]).Description; + } + + entityInfos.Add(new EntityInfo() + { + EntityName = c.Name, + DbTableName = sugarAttribute == null ? c.Name : ((SugarTable)sugarAttribute).TableName, + TableDescription = description, + Type = c + }); + } + + return await Task.FromResult(entityInfos); + + bool IsMyAttribute(Attribute[] o) + { + return o.Any(a => a.GetType() == type); + } + } + + /// + /// 获取实体模板文件路径 + /// + /// + private static string GetEntityTemplatePath() + { + var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template"); + return Path.Combine(templatePath, "Entity.cs.vm"); + } + + /// + /// 获取种子数据模板文件路径 + /// + /// + private static string GetSeedDataTemplatePath() + { + var templatePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "template"); + return Path.Combine(templatePath, "SeedData.cs.vm"); + } + + /// + /// 设置生成实体文件路径 + /// + /// + /// + private static string GetEntityTargetPath(CreateEntityInput input) + { + var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, input.Position, "Entity"); + if (!Directory.Exists(backendPath)) + Directory.CreateDirectory(backendPath); + return Path.Combine(backendPath, input.EntityName + ".cs"); + } + + /// + /// 设置生成种子数据文件路径 + /// + /// + /// + private static string GetSeedDataTargetPath(CreateSeedDataInput input) + { + var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, input.Position, "SeedData"); + if (!Directory.Exists(backendPath)) + Directory.CreateDirectory(backendPath); + return Path.Combine(backendPath, input.SeedDataName + ".cs"); + } + + /// + /// 备份数据库(PostgreSQL)🔖 + /// + /// + [HttpPost, NonUnify] + [DisplayName("备份数据库(PostgreSQL)")] + public async Task BackupDatabase() + { + if (_db.CurrentConnectionConfig.DbType != SqlSugar.DbType.PostgreSQL) + throw Oops.Oh("只支持 PostgreSQL 数据库 😁"); + + var npgsqlConn = new NpgsqlConnectionStringBuilder(_db.CurrentConnectionConfig.ConnectionString); + if (npgsqlConn == null || string.IsNullOrWhiteSpace(npgsqlConn.Host) || string.IsNullOrWhiteSpace(npgsqlConn.Username) || string.IsNullOrWhiteSpace(npgsqlConn.Password) || + string.IsNullOrWhiteSpace(npgsqlConn.Database)) + throw Oops.Oh("PostgreSQL 数据库配置错误"); + + // 确保备份目录存在 + var backupDirectory = Path.Combine(Directory.GetCurrentDirectory(), "backups"); + Directory.CreateDirectory(backupDirectory); + + // 构建备份文件名 + string backupFileName = $"backup_{DateTime.Now:yyyyMMddHHmmss}.sql"; + string backupFilePath = Path.Combine(backupDirectory, backupFileName); + + // 启动pg_dump进程进行备份 + // 设置密码:export PGPASSWORD='xxxxxx' + var bash = $"-U {npgsqlConn.Username} -h {npgsqlConn.Host} -p {npgsqlConn.Port} -E UTF8 -F c -b -v -f {backupFilePath} {npgsqlConn.Database}"; + var startInfo = new ProcessStartInfo + { + FileName = "pg_dump", + Arguments = bash, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + EnvironmentVariables = + { + ["PGPASSWORD"] = npgsqlConn.Password + } + }; + + //_logger.LogInformation("备份数据库:pg_dump " + bash); + + //try + //{ + using (var backupProcess = Process.Start(startInfo)) + { + await backupProcess.WaitForExitAsync(); + + //var output = await backupProcess.StandardOutput.ReadToEndAsync(); + //var error = await backupProcess.StandardError.ReadToEndAsync(); + + // 检查备份是否成功 + if (backupProcess.ExitCode != 0) + { + throw Oops.Oh($"备份失败:ExitCode({backupProcess.ExitCode})"); + } + } + + // _logger.LogInformation($"备份成功:{backupFilePath}"); + //} + //catch (Exception ex) + //{ + // _logger.LogError(ex, $"备份失败:"); + // throw; + //} + + // 若备份成功则提供下载链接 + return new FileStreamResult(new FileStream(backupFilePath, FileMode.Open), "application/octet-stream") + { + FileDownloadName = backupFileName + }; + } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm deleted file mode 100644 index dcdf3eae..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Dto.cs.vm +++ /dev/null @@ -1,30 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace @Model.NameSpace; - - /// - /// @(@Model.BusName)输出参数 - /// - public class @(@Model.ClassName)Dto - { -@foreach (var column in Model.TableField){ -if(@column.EffectType == "ForeignKey" && @column.FkEntityName != "" && @column.FkColumnName != ""){ - @:/// - @:/// @column.ColumnComment - @:/// - @:public @(@column.FkColumnNetType) @(@column.PropertyName)@(@column.FkColumnName) { get; set; } - @: -} -} -@foreach (var column in Model.TableField){ - @:/// - @:/// @column.ColumnComment - @:/// - @:public @column.NetType @column.PropertyName { get; set; } - @: -} - } diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm index 52565d29..72a1dd3c 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/Entity.cs.vm @@ -4,9 +4,11 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -@if(@Model.BaseClassName!=""){ -@:using Admin.NET.Core; -} +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "Admin.NET") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// namespace @Model.NameSpace; @@ -14,86 +16,75 @@ namespace @Model.NameSpace; /// @(@Model.Description) /// [SugarTable(null, "@(@Model.Description)")] -@if(@Model.ConfigId!="1300000000001"){ +@if (Model.ConfigId != "1300000000001") { @:[Tenant("@(@Model.ConfigId)")] } -public class @(@Model.EntityName) @Model.BaseClassName +public partial class @(Model.EntityName) @(Model.BaseClassName) { -@foreach (var column in Model.TableField){ -if(@Model.BaseClassName=="" && @column.IsPrimarykey){ +@foreach (var column in Model.TableFields) { +if (Model.BaseClassName == "" && column.IsPrimarykey) { @:/// @:/// @column.ColumnDescription @:/// - if(!@column.IsNullable){ + if (!column.IsNullable) { @:[Required] } - if(@column.DataType=="string"||@column.DataType=="string?"){ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length)] + if (column.DataType == "string" || column.DataType=="string?") { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true, Length = @column.Length)] + } else if (column.DataType == "decimal" || column.DataType == "decimal?") { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )] + } else { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true)] } - else if(@column.DataType=="decimal"||@column.DataType=="decimal?"){ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )] - } - else{ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true)] - } - @:public @column.DataType @column.PropertyName { get; set; } + @:public virtual @column.DataType @column.PropertyName { get; set; } @: -} -else if(@Model.BaseClassName=="" && !@column.IsPrimarykey){ +} else if (Model.BaseClassName == "" && !column.IsPrimarykey) { @:/// @:/// @column.ColumnDescription @:/// - if(!@column.IsNullable){ + if(!column.IsNullable){ @:[Required] } - if(@column.DataType=="string"||@column.DataType=="string?"){ + if (column.DataType == "string" || column.DataType == "string?") { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length)] - } - else if(@column.DataType=="decimal"||@column.DataType=="decimal?"){ + } else if (column.DataType == "decimal" || column.DataType=="decimal?") { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length, DecimalDigits=@column.DecimalDigits )] - } - else{ + } else { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription")] } - @:public @column.DataType @column.PropertyName { get; set; } + @:public virtual @column.DataType @column.PropertyName { get; set; } @: -} -else if(@Model.BaseClassName!="" && @column.IsPrimarykey && @column.DbColumnName.ToLower()!="id"){ +} else if (Model.BaseClassName!="" && column.IsPrimarykey && column.DbColumnName.ToLower()!="id") { @:/// @:/// @column.ColumnDescription @:/// - if(!@column.IsNullable){ + if (!column.IsNullable) { @:[Required] } - if(@column.DataType=="string"||@column.DataType=="string?"){ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length)] + if (column.DataType == "string" || column.DataType == "string?") { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true, Length = @column.Length)] + } else if (column.DataType == "decimal" || column.DataType == "decimal?") { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )] + } else { + @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimarykey = true)] } - else if(@column.DataType=="decimal"||@column.DataType=="decimal?"){ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true, Length = @column.Length, DecimalDigits=@column.DecimalDigits )] - } - else{ - @:[SugarColumn(ColumnName = "@column.DbColumnName", IsIdentity = @column.IsIdentity.ToString().ToLower(), ColumnDescription = "@column.ColumnDescription", IsPrimaryKey = true)] - } - @:public @column.DataType @column.PropertyName { get; set; } + @:public virtual @column.DataType @column.PropertyName { get; set; } @: -} -else if(@Model.BaseClassName!="" && !@column.IsPrimarykey && @column.DbColumnName.ToLower()!="id"){ +} else if (Model.BaseClassName != "" && !column.IsPrimarykey && column.DbColumnName.ToLower() != "id") { @:/// @:/// @column.ColumnDescription @:/// - if(!@column.IsNullable){ + if (!column.IsNullable) { @:[Required] } - if(@column.DataType=="string"||@column.DataType=="string?"){ + if(column.DataType == "string" || column.DataType == "string?") { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length)] - } - else if(@column.DataType=="decimal"||@column.DataType=="decimal?"){ + } else if (column.DataType == "decimal" || column.DataType == "decimal?") { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription", Length = @column.Length, DecimalDigits=@column.DecimalDigits )] - } - else{ + } else { @:[SugarColumn(ColumnName = "@column.DbColumnName", ColumnDescription = "@column.ColumnDescription")] } - @:public @column.DataType @column.PropertyName { get; set; } + @:public virtual @column.DataType @column.PropertyName { get; set; } @: } } diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialEntity_Entity.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialEntity_Entity.cs.vm deleted file mode 100644 index 5eb86132..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialEntity_Entity.cs.vm +++ /dev/null @@ -1,56 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -@if(@Model.NameSpace != "Admin.NET.Core"){ -@:using Admin.NET.Core; -} -namespace @(@Model.NameSpace); -/// -/// 扩展@(@Model.BusName)输出实体 -/// -public partial class @(@Model.ClassName)Output -{ - - - /// - /// count - /// - public int count { get; set; } - - @if(Model.TableField.FirstOrDefault(u => u.ColumnName.ToLower() == "name") == null){ - @:/// - @:/// Name - @:/// - @:public string Name { get; set; } - } -@if(Model.TabType=="Tree"){ - @:/// - @:/// @(@Model.BusName)子项 - @:/// - @:[SugarColumn(IsIgnore = true)] - @:public List<@(@Model.ClassName)Output> Children { get; set; } - - @:/// - @:/// 是否禁止选中 - @:/// - @:[SugarColumn(IsIgnore = true)] - @:public bool Disabled { get; set; } -} - -} - -/// -/// 扩展@(@Model.BusName)分页查询输入参数 -/// -public partial class Page@(@Model.ClassName)Input -{ - @if(@Model.TableField.Any(x=>x.Statistical == "Y")){ - @:public string[] GroupBy { get; set; } - @:public string[] Sum { get; set; } - @:public IEnumerable Aggregations { get; set; } - } - -} diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialService_Service.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialService_Service.cs.vm deleted file mode 100644 index ae39bc86..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/PartialService_Service.cs.vm +++ /dev/null @@ -1,76 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using Microsoft.AspNetCore.Http; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -@{ - string joinTableName = "u"; - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - string RemoteField=""; - string PKName=""; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - if(column.RemoteVerify){ - RemoteField=@column.PropertyName; - } - if(column.ColumnKey == "True"){ - PKName=column.PropertyName; - } - } -} -@if(@Model.NameSpace != "Admin.NET.Core"){ -@:using Admin.NET.Core; -} - -namespace @Model.NameSpace; -/// -/// 扩展@(@Model.BusName)服务 -/// -public partial class @(@Model.ClassName)Service -{ - - /// - /// 获取列表 🔖 - /// - /// - /// - [ApiDescriptionSettings(Name = "GetTreeList", Description = "获取列表", Order = 1100), HttpPost] - [DisplayName("获取列表")] - public async Task> GetTreeList(Page@(@Model.ClassName)Input input) - { - @if(Model.TabType=="Tree"){ - @:var list = await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).ToTreeAsync(u => u.Children, u => u.@(@Model.TreeKey), input.Id); - @://var list = await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).ToListAsync();//非树形结构表 - - @:var md = await _@(@Model.LowerClassName)Rep.AsQueryable().Where(u => u.Id == input.Id).Select<@(@Model.ClassName)Output>().FirstAsync(); - @:if (md == null) return list; - @: - @:md.Children = list; - @:list = [md]; - @:return list; - }else{ - @://return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).ToTreeAsync(u => u.Children, u => u.@(@Model.TreeKey), input.Id));//树形结构表 - @:return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).ToListAsync(); - } - } - -} -/// -/// 扩展@(@Model.BusName)中间件 -/// -public partial class @(@Model.ClassName)Mid -{ - -} -@{ -string LowerFirstLetter(string text) -{ -return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 -} -} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm index 016532b6..f3edff76 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/SeedData.cs.vm @@ -4,15 +4,18 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -using Admin.NET.Core; -using @Model.EntityNameSpace; +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "Admin.NET") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// namespace @Model.NameSpace; /// /// @(Model.Description) 表种子数据 /// -public class @(Model.SeedDataName): ISqlSugarEntitySeedData<@(Model.EntityName)> +public class @(Model.EntityName)SeedData : ISqlSugarEntitySeedData<@(Model.EntityName)> { /// /// 种子数据 diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm deleted file mode 100644 index 8e756ab6..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/data.data.ts.vm +++ /dev/null @@ -1,165 +0,0 @@ -import { h } from 'vue'; -import { BasicColumn, FormSchema } from '/@@/components/Table'; -import { useUserInfo } from '/@@/stores/userInfo'; - -@foreach (var column in Model.TableField){ -if(@column.EffectType == "Upload"){ -@:import { uploadFile } from '/@@/api/sys/admin'; -}else if(@column.EffectType == "ForeignKey"){ -@:import { get@(@column.FkEntityName)Dropdown } from '/@@/api/main/@(@Model.ClassName)'; -}else if(@column.EffectType == "DictSelector"){ -@:import { getDataList } from '/@@/api/sys/admin'; -}else if(@column.EffectType == "ApiTreeSelector"){ -@:import { get@(@column.FkEntityName)Tree } from '/@@/api/main/@(@Model.ClassName)'; -}else if(@column.EffectType == "Switch"){ -@:import { Switch } from 'ant-design-vue'; -}else if(@column.EffectType == "ConstSelector"){ -@:const codeToName = useUserInfo().codeToName; -@:const getConstType = useUserInfo().getConstDataByTypeCode; -} -} - -export const columns: BasicColumn[] = [ - @foreach (var column in Model.TableField){ - if(@column.WhetherTable == "Y"){ - @:{ - @:title: '@column.ColumnComment', - @:dataIndex: '@column.LowerPropertyName', - @:sorter: true, -if(@column.EffectType == "Upload"){ - @:slots: { customRender: '@(@column.LowerPropertyName)' }, -}else if(@column.EffectType == "ForeignKey"){ - @:customRender: ({ record }) => { - @:return record.fk@(@column.PropertyName).@(@column.LowerFkColumnName); - @:}, -}else if(@column.EffectType == "Switch"){ - @:customRender: ({ record }) => { - @:return h(@(@column.EffectType), { checked: record.@(@column.LowerPropertyName) }); - @:}, -}else if(@column.EffectType == "ConstSelector"){ - @:customRender: ({ record }) => { - @:return codeToName(record.@(@column.LowerPropertyName), '@(@column.DictTypeCode)'); - @:}, -} - @:}, - } - - } -]; - -export const searchFormSchema: FormSchema[] = [ -@foreach (var column in Model.QueryWhetherList){ - @:{ - @:field: '@column.LowerPropertyName', - @:label: '@column.ColumnComment', - @:colProps: { span: 8 }, -if(@column.EffectType == "ForeignKey"){ - @:component: 'ApiSelect', - @:componentProps: { - @:api: get@(@column.FkEntityName)Dropdown, - @:labelField: 'label', - @:valueField: 'value', - @:}, -}else if(@column.EffectType == "DictSelector"){ - @:component: 'ApiSelect', - @:componentProps: { - @:api: getDataList, - @:params: '@(@column.DictTypeCode)', - @:fieldNames: { - @:label: 'label', - @:value: 'value', - @:}, - @:}, -}else if(@column.EffectType == "ConstSelector"){ - @:component: 'Select', - @:componentProps: { - @:options: getConstType('@(@column.DictTypeCode)'), - @:fieldNames: { - @:label: 'name', - @:value: 'code', - @:}, - @:}, -}else if(@column.EffectType == "ApiTreeSelector"){ - @:component: '@(@column.EffectType)', - @:componentProps: { - @:api: get@(@column.FkEntityName)Tree, - @:}, -} -else if(@column.NetType?.TrimEnd('?') == "DateTime" && @column.QueryType == "~"){ - @:component: 'RangePicker', - @:componentProps: { - @: valueFormat:"YYYY-MM-DD" - @:}, -} -else{ - @:component: 'Input', -} - - @:}, -} -]; - -export const formSchema: FormSchema[] = [ - @foreach (var column in Model.TableField){ - @:{ - @:label: '@column.ColumnComment', - @:field: '@column.LowerPropertyName', -if(@column.EffectType == "ForeignKey"){ - @:component: 'ApiSelect', - @:componentProps: { - @:api: get@(@column.FkEntityName)Dropdown, - @:labelField: 'label', - @:valueField: 'value', - @:}, -}else if(@column.EffectType == "DictSelector"){ - @:component: 'ApiSelect', - @:componentProps: { - @:api: getDataList, - @:params: '@(@column.DictTypeCode)', - @:fieldNames: { - @:label: 'label', - @:value: 'value', - @:}, - @:}, -}else if(@column.EffectType == "ConstSelector"){ - @:component: 'Select', - @:componentProps: { - @:options: getConstType('@(@column.DictTypeCode)'), - @:fieldNames: { - @:label: 'name', - @:value: 'code', - @:}, - @:}, -}else if(@column.EffectType == "ApiTreeSelector"){ - @:component: '@(@column.EffectType)', - @:componentProps: { - @:api: get@(@column.FkEntityName)Tree, - @:}, -}else if(@column.EffectType == "Switch"){ - @:component: '@(@column.EffectType)', - @:componentProps: { - @:checkedChildren: '是', - @:unCheckedChildren: '否', - @:}, -}else{ - @:component: '@column.EffectType', -} - if(@column.WhetherRequired == "Y"){ - @:required: true, - }else{ - @:required: false, - } - if(@column.EffectType == "Upload"){ - @:componentProps: { - @:maxNumber: 1, - @:api: uploadFile, - @:}, - } - if(@column.LowerPropertyName == "id"){ - @:show: false, - } - @:colProps: { span: 12 }, - @:}, - - } -]; diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/dataModal.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/dataModal.vue.vm deleted file mode 100644 index 25db585d..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/dataModal.vue.vm +++ /dev/null @@ -1,71 +0,0 @@ - - diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_InputDto.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_InputDto.cs.vm index a35291df..135d9111 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_InputDto.cs.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_InputDto.cs.vm @@ -4,222 +4,259 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -using Admin.NET.Core; -using System.ComponentModel.DataAnnotations; -@{ - string RemoteField=""; - string PKName=""; - foreach (var column in Model.TableField){ - if(column.RemoteVerify){ - RemoteField=@column.PropertyName; - } - if(column.ColumnKey == "True"){ - PKName=column.PropertyName; - } - } -} +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "Admin.NET") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// namespace @Model.NameSpace; - /// - /// @(@Model.BusName)基础输入参数 - /// - public class @(@Model.ClassName)BaseInput - { -@foreach (var column in Model.TableField){ -if (@column.ColumnKey != "True"){ - - @:/// - @:/// @column.ColumnComment - @:/// - @:public virtual @column.NetType @column.PropertyName { get; set; } - @: -} -} - } - - /// - /// @(@Model.BusName)分页查询输入参数 - /// - public partial class Page@(@Model.ClassName)Input : BasePageInput - { - /// - /// 关键字查询 - /// - public string? SearchKey { get; set; } - -@foreach (var column in Model.TableField){ - if (@column.QueryWhether == "Y"){ - - @:/// - @:/// @column.ColumnComment - @:/// - if(@column.NetType?.EndsWith("?") == true){ - @:public @column.NetType @column.PropertyName { get; set; } - }else { - @:public @(@column.NetType)? @column.PropertyName { get; set; } - } - @: - - if(@column.NetType?.TrimEnd('?') == "DateTime" && @column.QueryType == "~"){ - @:/// - @: /// @(@column.ColumnComment)范围 - @: /// - @: public DateTime?[] @(@column.PropertyName)Range { get; set; } - -} - -} -} - } - - /// - /// @(@Model.BusName)增加输入参数 - /// - public class Add@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput - { -@foreach (var column in Model.TableField){ -if (@column.WhetherAddUpdate == "Y"){ - @:/// - @:/// @column.ColumnComment - @:/// - @if(@column.WhetherRequired=="Y"){ - @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")] - } - @if(@column.NetType.StartsWith("string")&&!@column.RuleItems.Any(t=>t.Type=="length")&&@column.ColumnLength>0){ - @:[StringLength(@(@column.ColumnLength), ErrorMessage = "@(@column.ColumnComment)不能超过@(@column.ColumnLength)个字符")] - } - @if(@column.AnyRule){ - @foreach(var rule in @column.RuleItems){ - @if(rule.Type=="pattern"){ - @:[RegularExpression(@@"@(@FormatPattern(rule.Pattern))", ErrorMessage = "@(@rule.Message)")] - } - @if(rule.Type=="length"&&column.NetType.StartsWith("string")){ - @:[StringLength(@(@rule.Max),MinimumLength = @(@rule.Min), ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)至@(@rule.Max)字符")] - } - @if(rule.Type=="length"&&column.NetType.StartsWith("DateTime")){ - @:[Range(typeof(DateTime), "@(@rule.Min)", "@(@rule.Max)", ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)到@(@rule.Max)")] - } - @if(rule.Type=="length"&&(column.NetType.StartsWith("int")||column.NetType.StartsWith("long")||column.NetType.StartsWith("decimal")||column.NetType.StartsWith("float")||column.NetType.StartsWith("double"))){ - @:[Range(@(@rule.Min), @(@rule.Max), ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)至@(@rule.Max)")] - } - } - } - @:public override @column.NetType @column.PropertyName { get; set; } - @: -} -} - } - - /// - /// @(@Model.BusName)删除输入参数 - /// - public class Delete@(@Model.ClassName)Input : BaseIdInput - { -@foreach (var column in Model.TableField){ -if (@column.NetType == "Guid" && @column.PropertyName == "Id"){ - @:/// - @:/// @column.ColumnComment - @:/// - @:public @column.NetType @column.PropertyName { get; set; } - @: -} -} -@foreach (var column in Model.TableField){ -if (@column.ColumnKey == "True" && @column.PropertyName != "Id"){ - @:/// - @:/// @column.ColumnComment - @:/// - @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")] - @:public @column.NetType @column.PropertyName { get; set; } - @: -} -} - } - - /// - /// @(@Model.BusName)更新输入参数 - /// - public class Update@(@Model.ClassName)Input : @(@Model.ClassName)BaseInput - { -@foreach (var column in Model.TableField){ -if (@column.ColumnKey == "True"){ - @:/// - @:/// @column.ColumnComment - @:/// - @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")] - @if (@column.NetType == "Guid" && @column.PropertyName == "Id"){ - @:[AdaptIgnore] - } - @:public @column.NetType @column.PropertyName { get; set; } - @: -} -if (@column.WhetherAddUpdate == "Y"){ - @:/// - @:/// @column.ColumnComment - @:/// - @if(@column.WhetherRequired=="Y"){ - @:[Required(ErrorMessage = "@(@column.ColumnComment)不能为空")] - } - @if(@column.NetType.StartsWith("string")&&!@column.RuleItems.Any(t=>t.Type=="length")&&@column.ColumnLength>0){ - @:[StringLength(@(@column.ColumnLength), ErrorMessage = "@(@column.ColumnComment)不能超过@(@column.ColumnLength)个字符")] - } - @if(@column.AnyRule){ - @foreach(var rule in @column.RuleItems){ - @if(rule.Type=="pattern"){ - @:[RegularExpression(@@"@(@FormatPattern(rule.Pattern))", ErrorMessage = "@(@rule.Message)")] - } - @if(rule.Type=="length"&&column.NetType.StartsWith("string")){ - @:[StringLength(@(@rule.Max),MinimumLength = @(@rule.Min), ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)至@(@rule.Max)字符")] - } - @if(rule.Type=="length"&&column.NetType.StartsWith("DateTime")){ - @:[Range(typeof(DateTime), "@(@rule.Min)", "@(@rule.Max)", ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)到@(@rule.Max)")] - } - @if(rule.Type=="length"&&(column.NetType.StartsWith("int")||column.NetType.StartsWith("long")||column.NetType.StartsWith("decimal")||column.NetType.StartsWith("float")||column.NetType.StartsWith("double"))){ - @:[Range(@(@rule.Min), @(@rule.Max), ErrorMessage = "@(@column.ColumnComment)只能是@(@rule.Min)至@(@rule.Max)")] - } - } - } - @:public override @column.NetType @column.PropertyName { get; set; } - @: -} -} - } - - /// - /// @(@Model.BusName)主键查询输入参数 - /// - public class QueryById@(@Model.ClassName)Input : Delete@(@Model.ClassName)Input - { - - } - - -@if(@Model.RemoteVerify){ - @:/// - @:/// 检查@(RemoteField)字段参数 - @:/// - @:public class Exists@(RemoteField)Input - @:{ - @:/// - @:/// 字段值 - @:/// - @:public string FieldValue { get; set; } - @: - @:/// - @:/// 旧字段值 - @:/// - @:public string OldFieldValue { get; set; } - @:} -} -@{ -string FormatPattern(string pattern) +/// +/// @(Model.BusName)基础输入参数 +/// +public class @(Model.ClassName)BaseInput { - if (pattern.EndsWith('g')) - { - pattern = pattern.TrimEnd('g'); +@{ var i = 0; } +@foreach (var column in Model.AllFields){ +if (column.IsPrimarykey) continue; +if (!@column.IsCommon) { + if (i++ != 0) { + @: } - pattern = pattern.Trim('/'); - return pattern; + @:/// + @:/// @column.ColumnComment + @:/// + + if (column.IsDict) { + @:[Dict("@(column.DictConfig?.Code)")] + } + + if (column.IsEnum) { + @:[Dict(nameof(@column.DictConfig?.Code))] + } + + @:public virtual @column.NullableNetType @column.PropertyName { get; set; } } +} +} + +/// +/// @(@Model.BusName)分页查询输入参数 +/// +public class Page@(@Model.ClassName)Input : BasePageInput +{ +@{ i = 0; } +@foreach (var column in Model.QueryFields) { + if (i++ != 0) { + @: + } + @:/// + @:/// @column.ColumnComment + @:/// + + if (column.IsConst) { + @:[Const(nameof(@column.DictConfig?.Code))] + } + + if (column.IsDict) { + @:[Dict("@(column.DictConfig?.Code)")] + } + + if (column.IsEnum) { + @:[Dict(nameof(@column.DictConfig?.Code))] + } + + @:public @column.NullableNetType @column.PropertyName { get; set; } + + if (@column.NetType?.TrimEnd('?') == "DateTime" && @column.QueryType == "~") { + @: + @:/// + @:/// @(column.ColumnComment)范围 + @:/// + @:public DateTime?[] @(column.PropertyName)Range { get; set; } +} +} +} + +/// +/// @(Model.BusName)增加输入参数 +/// +public class Add@(Model.ClassName)Input : @(Model.ClassName)BaseInput +{ +@{ i = 0; } +@foreach (var column in Model.AddUpdateFields.Where(u => u.IsRequired)) { + if (i++ != 0) { + @: + } + @:/// + @:/// @column.ColumnComment + @:/// + + if (@column.IsRequired) { + @:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")] + } else if (column.FromValid != null && (int)column.FromValid != 100) { + @:@(column.GetFromValidAttribute()) + } + + if (column.IsConst) { + @:[Const(nameof(@column.DictConfig?.Code))] + } + + if (column.IsDict) { + @:[Dict("@(column.DictConfig?.Code)"@(column.IsRequired ? "" : ""))] + } else if (column.IsEnum) { + @:[Dict(nameof(@column.DictConfig?.Code)@(column.IsRequired ? "" : ""))] + } + + @:public override @column.NullableNetType @column.PropertyName { get; set; } +} +} + +/// +/// @(Model.BusName)主键输入参数 +/// +public class Primarykey@(Model.ClassName)Input +{ +@foreach (var column in Model.PrimaryFields){ + @:/// + @:/// @column.ColumnComment + @:/// + @:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")] + if (@column.NetType == "Guid" && @column.PropertyName == "Id") { + @:public @column.NullableNetType @column.PropertyName { get; set; } + } else if (@column.IsPrimarykey) { + @:public @column.NullableNetType @column.PropertyName { get; set; } + } +} +} + +/// +/// @(@Model.BusName)更新输入参数 +/// +public class Update@(Model.ClassName)Input : Add@(Model.ClassName)Input +{ +@foreach (var column in Model.PrimaryFields){ + @:/// + @:/// @column.ColumnComment + @:/// + @:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")] + if (@column.NetType == "Guid" && @column.PropertyName == "Id") { + @:public @column.NullableNetType @column.PropertyName { get; set; } + } else if (@column.IsPrimarykey) { + @:public @column.NullableNetType @column.PropertyName { get; set; } + } +} +} + +@foreach (var column in Model.ApiFields.GroupBy(u => new { u.IsTree, u.JoinConfig.EntityName }).Select(u => u.First()).ToList()) { +var methodName = (column.IsTree ? "Tree" : "Page") + Model.ClassName + column.JoinConfig.EntityName; +@:/// +@:/// 获取@(column.JoinConfig.TableComment)数据输入参数 +@:/// +@:public class @(methodName)Input : Base@(column.IsTree ? "Dropdown" : "Page")Input +@:{ + if (!column.IsTree) { + @:/// + @:/// 数据来自于表 + @:/// + @:public bool All { get; set; } + } +@:} +@: +} +@if (Model.HasStatus) { +@:/// +@:/// 设置状态输入参数 +@:/// +@:public class Set@(Model.ClassName)StatusInput : BaseStatusInput +@:{ +foreach (var column in Model.AllFields.Where(u => u.IsPrimarykey && (u.NetType == "Guid" && u.PropertyName == "Id" || !(u.NetType == "long" && u.PropertyName == "Id")))) { + @:/// + @:/// @column.ColumnComment + @:/// + @:[Required(ErrorMessage = "@(column.ColumnComment)不能为空")] + if (column.NetType == "Guid" && column.PropertyName == "Id") { + @:public new @column.NullableNetType @column.PropertyName { get; set; } + } else if (column.IsPrimarykey) { + @:public @column.NullableNetType @column.PropertyName { get; set; } + } +} +@:} +@: +} +/// +/// @(Model.BusName)主键查询输入参数 +/// +public class QueryById@(@Model.ClassName)Input : Primarykey@(Model.ClassName)Input +{ +} + +@if (Model.ImportFields?.Count > 0){ +@:/// +@:/// @(Model.BusName)主键查询输入参数 +@:/// +@:public class Export@(@Model.ClassName)Input : Page@(@Model.ClassName)Input +@:{ + @:/// + @:/// 需要导入的主键集 + @:/// + @:[ImporterHeader(IsIgnore = true)] + @:[ExporterHeader(IsIgnore = true)] + @:public List<@(Model.PrimaryFields.First().NetType)> SelectKeyList { get; set; } +@:} +@: +@:/// +@:/// @(Model.BusName)数据导入实体 +@:/// +@:[ExcelImporter(SheetIndex = 1, IsOnlyErrorRows = true)] +@:public class Import@(Model.ClassName)Input : BaseImportInput +@:{ +foreach (var column in Model.AllFields) { + if (@column.NetType == "Guid" && @column.PropertyName == "Id") { + @:/// + @:/// 记录Id + @:/// + @:[ImporterHeader(IsIgnore = true)] + @:[ExporterHeader(IsIgnore = true)] + @:public new @column.NullableNetType @column.PropertyName { get; set; } + @: + } +} +foreach (var column in Model.ImportFields) { + var headerName = (column.IsRequired ? "*" : "") + column.ColumnComment; + if (column.HasJoinTable || column.IsDict) { + @:/// + @:/// @column.ColumnComment 关联值 + @:/// + @:[ImporterHeader(IsIgnore = true)] + @:[ExporterHeader(IsIgnore = true)] + @:public @column.NullableNetType @column.PropertyName { get; set; } + @: + @:/// + @:/// @column.ColumnComment 文本 + @:/// + if (column.IsDict) { + @:[Dict("@(column.DictConfig?.Code)"@(column.IsRequired ? "" : ""))] + } + @:[ImporterHeader(Name = "@(headerName)")] + @:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)] + @:public string @(column.PropertyName + "Label") { get; set; } + } else { + @:/// + @:/// @column.ColumnComment + @:/// + if (column.IsConst) { + @:[Const(nameof(@column.DictConfig?.Code))] + } + if (column.IsEnum) { + @:[Dict(nameof(@column.DictConfig?.Code)@(column.IsRequired ? "" : ""))] + } + @:[ImporterHeader(Name = "@(headerName)")] + @:[ExporterHeader("@(headerName)", Format = "@", Width = 25, IsBold = true)] + @:public @column.NullableNetType @column.PropertyName { get; set; } + } + @: + } +@:} } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Mid.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Mid.cs.vm deleted file mode 100644 index 305eecd3..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Mid.cs.vm +++ /dev/null @@ -1,129 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -@{ - string joinTableName = "u"; - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - string RemoteField=""; - string PKName=""; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - if(column.RemoteVerify){ - RemoteField=@column.PropertyName; - } - if(column.ColumnKey == "True"){ - PKName=column.PropertyName; - } - } -} -@if(@Model.NameSpace != "Admin.NET.Core"){ -@:using Admin.NET.Core; -} -using Admin.NET.Core.Service; - -namespace @(@Model.NameSpace); -public partial class @(@Model.ClassName)Mid -{ - /// - /// 获取查询 - /// - /// - /// - /// - public static ISugarQueryable<@(@Model.ClassName)> GetQuery(SqlSugarRepository<@(@Model.ClassName)> _@(@Model.LowerClassName)Rep,Page@(@Model.ClassName)Input input) - { - var sysCacheService = App.GetRequiredService(); - var db = App.GetRequiredService(); - @if (haveLikeCdt) { - @:input.SearchKey = input.SearchKey?.Trim(); - } - var query = _@(@Model.LowerClassName)Rep.AsQueryable() - @{string conditionFlag = "";} - @if (haveLikeCdt) { - @:.WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => - @foreach (var column in Model.TableField){ - if (@column.QueryWhether == "Y" && column.QueryType == "like"){ - @:@(conditionFlag)u.@(@column.PropertyName).Contains(input.SearchKey) - conditionFlag="|| "; - } - } - @:) - } - @foreach (var column in Model.TableField){ - if (@column.QueryWhether == "Y"){ - if (@column.NetType?.TrimEnd('?') == "string"){ - if(@column.QueryType == "like"){ - @:.WhereIF(!string.IsNullOrWhiteSpace(input.@column.PropertyName), u => u.@(@column.PropertyName).Contains(input.@(@column.PropertyName).Trim())) - }else{ - @:.WhereIF(!string.IsNullOrWhiteSpace(input.@column.PropertyName), u => u.@(@column.PropertyName) @column.QueryType input.@(@column.PropertyName)) - } - }else if(@column.NetType?.TrimEnd('?') == "int" || @column.NetType?.TrimEnd('?') == "long"){ - @:.WhereIF(input.@column.PropertyName>0, u => (int)u.@(@column.PropertyName) @column.QueryType input.@(@column.PropertyName)) - }else if(@column.NetType?.TrimEnd('?') == "DateTime" && @column.QueryType == "~"){ - @:.WhereIF(input.@(@column.PropertyName)Range != null && input.@(@column.PropertyName)Range.Length == 2, u => u.@(@column.PropertyName) >= input.@(@column.PropertyName)Range[0] && u.@(@column.PropertyName) <= input.@(@column.PropertyName)Range[1]) - }else if(@column.NetType?.TrimEnd('?').EndsWith("Enum") == true) { - @:.WhereIF(input.@(@column.PropertyName).HasValue, u => u.@(@column.PropertyName) @column.QueryType input.@(@column.PropertyName)) - } - } - if(@column.PropertyName == "IsDelete") { - @:.Where( u => u.IsDelete == false) - } - } - @if (Model.TableField.Any(c => c.PropertyName == "OrderNo")) - { - @:.OrderBy(u => u.OrderNo) - } - - @if(!string.IsNullOrEmpty(Model.TreeName)){ - @:.Mapper(c => c.Name= c.@(@Model.TreeName).ToString()) - } - @foreach (var column in Model.TableField){ - if(@column.EffectType == "Upload"){ - @://.Mapper(c => c.@(@column.PropertyName)Attachment, c => c.@(@column.PropertyName)) - } - else if(@column.EffectType == "ForeignKey"){ - @:.Mapper(t => - @:{ - @: //使用缓存 - @: var key = $"@(@column.FkEntityName)_{t.@(@column.PropertyName)}"; - @: if (!sysCacheService.ExistKey(key)) - @: { - @: var m = db.ForTenant<@(@column.FkEntityName)>().Queryable<@(@column.FkEntityName)>().First(f => f.@(@column.FkLinkColumnName) == t.@(@column.PropertyName)); - @: if (m != null) sysCacheService.Set(key, m); - @: } - @: t.@(@column.PropertyName)@(@column.FkColumnName) = sysCacheService.Get<@(@column.FkEntityName)>(key)?.@(@column.FkColumnName); - @: //t.@(@column.PropertyName)@(@column.FkColumnName)=db.Queryable<@(@column.FkEntityName)>().First(f => f.@(@column.FkLinkColumnName) == t.@(@column.PropertyName))).@(@column.FkColumnName);// - @:}) - } - else if(@column.EffectType == "ApiTreeSelector"){ - @:.Mapper(t => - @:{ - @: //使用缓存 - @: var key = $"@(@column.FkEntityName)_{t.@(@column.PropertyName)}"; - @: if (!sysCacheService.ExistKey(key)) - @: { - @: var m = db.ForTenant<@(@column.FkEntityName)>().Queryable<@(@column.FkEntityName)>().First(f => f.@(@column.ValueColumn) == t.@(@column.PropertyName)); - @: if (m != null) sysCacheService.Set(key, m); - @: } - @: t.@(@column.PropertyName)@(@column.DisplayColumn) = sysCacheService.Get<@(@column.FkEntityName)>(key)?.@(@column.DisplayColumn); - @: //t.@(@column.PropertyName)@(@column.FkColumnName)=db.Queryable<@(@column.FkEntityName)>().FirstAsync(f => f.@(@column.FkLinkColumnName) == t.@(@column.PropertyName))).@(@column.FkColumnName);// - @:}) - } - } - ; - - return query; - } -} -@{ -string LowerFirstLetter(string text) -{ -return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 -} -} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_OutputDto.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_OutputDto.cs.vm index 589911ed..46e923c3 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_OutputDto.cs.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_OutputDto.cs.vm @@ -4,110 +4,69 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "Admin.NET") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// + namespace @Model.NameSpace; /// -/// 这里做优化 如果是富文本不传入 -/// @(@Model.BusName)基类输出参数 +/// @(@Model.BusName)输出参数 /// -public partial class Base@(@Model.ClassName)Output +public class Page@(@Model.ClassName)Output { -@foreach (var column in Model.TableField){ - +@foreach (var column in Model.AllFields) { @:/// @:/// @column.ColumnComment @:/// -if(column.EffectType == "ForeignKey") +if (column.IsForeignKey) { - @:public @column.NetType @column.PropertyName { get; set; } + @:public @column.NullableNetType @column.PropertyName { get; set; } @: @:/// @:/// @(column.ColumnComment) 描述 @:/// - @:public @(@column.FkColumnNetType) @(@column.PropertyName)@(@column.FkColumnName) { get; set; } + @:public string @(column.LeftJoinPropertyName) { get; set; } -}else if(column.EffectType == "Upload"){ - @:public @column.NetType @column.PropertyName { get; set; } - @:public SysFile @(@column.PropertyName)Attachment { get; set; } -}else if(column.EffectType == "ApiTreeSelector"){ - @:public @column.NetType @column.PropertyName { get; set; } +} else if (column.IsUpload) { + @:public @column.NullableNetType @column.PropertyName { get; set; } @: @:/// - @:/// 树型@(column.ColumnComment) 描述 + @:/// @(column.ColumnComment) 文件对象 @:/// - @:public string? @(@column.PropertyName)@(@column.DisplayColumn) { get; set; } + @:public SysFile @(column.LeftJoinPropertyName) { get; set; } + +} else if (column.IsTree) { + @:public @column.NullableNetType @column.PropertyName { get; set; } @: @:/// - @:/// 树型分类子项 + @:/// @(column.ColumnComment) 描述 @:/// - @:public List<@(@Model.ClassName)Output> Children { get; set; } = []; + @:public string @(column.LeftJoinPropertyName) { get; set; } -}else if(column.EffectType == "RichTextEditor"){ - @:/// HJ:富文本不传,提高性能 -}else{ - @:public @column.NetType @(@column.PropertyName) { get; set; } +} else { + @:public @column.NullableNetType @(@column.PropertyName) { get; set; } } - @: +@: } - } - -/// -/// @(@Model.BusName)分页输出参数 -/// -public partial class Page@(@Model.ClassName)Output : Base@(@Model.ClassName)Output -{ } /// -/// @(@Model.BusName)列表输出参数 +/// @(@Model.BusName)详情输出参数 /// -public partial class List@(@Model.ClassName)Output : Base@(@Model.ClassName)Output -{ +public class Detail@(@Model.ClassName)Output : Page@(@Model.ClassName)Output { } -@foreach (var column in Model.TableField){ -if (@column.EffectType == "ApiTreeSelector"){ - @:// 使用实际实体@(@column.FkTableName),所以这里就删了 - @:/* - @:[SugarTable("@(@column.FkTableName)")] - @:public class @(@column.FkEntityName)TreeOutput: EntityBaseId - @:{ - @:[SugarColumn(ColumnName = "@(@column.DisplayColumn)")] - @:public @(@Model.GetColumnNetType(@column.FkTableName,@column.DisplayColumn)) Label { get; set; } - - @:[SugarColumn(ColumnName = "@(@column.ValueColumn)", IsPrimaryKey = true, IsIdentity = false)] - @:public @(@Model.GetColumnNetType(@column.FkTableName,@column.ValueColumn)) Value { get; set; } - - @:public @(@Model.GetColumnNetType(@column.FkTableName,@column.PidColumn)) @column.PidColumn { get; set; } - @:[SugarColumn(IsIgnore = true)] - @:public List<@(@column.FkEntityName)TreeOutput> Children { get; set; } - @:} - @:*/ -} -} - - -/// -/// HJ:这里用的标准的Dto,如果有特殊需要可以在Dto中添加 -/// @(@Model.BusName)导入导出输出参数 -/// -public partial class @(@Model.ClassName)Dto -{ - -@foreach (var column in Model.TableField){ - - @:/// - @:/// @column.ColumnComment - @:/// - @:[ImporterHeader(Name = "@(column.ColumnComment)")] - @:[ExporterHeader(DisplayName = "@(column.ColumnComment)", IsBold = true)] - @:public @column.NetType @column.PropertyName { get; set; } - @: -} -} - - - - - - +@if (Model.ImportFields.Count > 0) { +@:/// +@:/// @(Model.BusName)数据导出模板实体 +@:/// +@:public class Export@(Model.ClassName)Output : Import@(Model.ClassName)Input +@:{ +@: [ImporterHeader(IsIgnore = true)] +@: [ExporterHeader(IsIgnore = true)] +@: public override string Error { get; set; } +@:} +} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Service.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Service.cs.vm index 04490f77..8aca4cba 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Service.cs.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/service_Service.cs.vm @@ -4,471 +4,463 @@ // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! -using Admin.NET.Core.Service; -using Microsoft.AspNetCore.Http; -using System.Linq.Expressions; -using Furion.EventBus; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -@{ - string joinTableName = "u"; - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - string RemoteField=""; - string PKName=""; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - if(column.RemoteVerify){ - RemoteField=@column.PropertyName; - } - if(column.ColumnKey == "True"){ - PKName=column.PropertyName; - } - } -} +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "Admin.NET") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// -namespace @Model.NameSpace; +namespace @(Model.NameSpace); /// -/// @(@Model.BusName)服务 +/// @(Model.BusName)服务 🧩 /// -[ApiDescriptionSettings(@(@Model.ProjectLastName)Const.GroupName, Name = "@(@Model.LowerClassName)", Order = 100)] -public partial class @(@Model.ClassName)Service : IDynamicApiController, ITransient +[ApiDescriptionSettings(@(Model.ProjectLastName)Const.GroupName, Order = 100)] +public class @(Model.ClassName)Service : IDynamicApiController, ITransient { - private readonly SysCacheService _sysCacheService;//默认CacheService - private readonly UserManager _userManager;//默认用户管理 - private readonly IEventPublisher _eventPublisher;//默认事件总线 - private readonly SqlSugarRepository<@(@Model.ClassName)> _@(@Model.LowerClassName)Rep; - @foreach (var column in Model.TableField){ - if(@column.EffectType == "ForeignKey"||@column.EffectType == "ApiTreeSelector"){ - if(@column.FkEntityName != @Model.ClassName){ - @:private readonly SqlSugarRepository<@(@column.FkEntityName)> _@(@column.LowerFkEntityName)Rep; - } + private readonly SqlSugarRepository<@(Model.ClassName)> _@(Model.LowerClassName)Rep; + @if (Model.ImportFields.Any()) { + @:private readonly SysDictTypeService _sysDictTypeService; + } + @if (Model.UploadFields.Any()) { + @:private readonly SysFileService _sysFileService; + } + @if (Model.DropdownFields.Any() || Model.UploadFields.Any()) { + @:private readonly ISqlSugarClient _db; + } + + public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(Model.ImportFields.Any() ? ", SysDictTypeService sysDictTypeService" : "")@(Model.DropdownFields.Any() ? ", ISqlSugarClient sqlSugarClient" : "")@(Model.UploadFields.Any() ? ", SysFileService sysFileService" : "")) + { + _@(Model.LowerClassName)Rep = @(Model.LowerClassName)Rep; + @if (Model.DropdownFields.Any()) { + @:_db = sqlSugarClient; + } + @if (Model.UploadFields.Any()) { + @:_sysFileService = sysFileService; + } + @if (Model.ImportFields.Any()) { + @:_sysDictTypeService = sysDictTypeService; + } + } + + /// + /// 分页查询@(Model.BusName) 🔖 + /// + /// + /// + [DisplayName("分页查询@(Model.BusName)")] + [ApiDescriptionSettings(Name = "Page"), HttpPost] + public async Task> Page(Page@(Model.ClassName)Input input) + { + input.Keyword = input.Keyword?.Trim(); + var query = _@(Model.LowerClassName)Rep.AsQueryable() +@{ + string joinTableName = "u"; + // 关键字模糊查询 + if (Model.QueryFields.Any(u => u.QueryType == "like")) { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => @string.Join(" || ", Model.QueryFields.Where(u => u.QueryType == "like").Select(col => $"u.{col.PropertyName}.Contains(input.Keyword)"))) + } + + // 单字段模糊查询 + foreach(var column in Model.QueryFields.Where(u => u.QueryType == "like")) { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName).Contains(input.@(column.PropertyName).Trim())) + } + + // 字段组合查询 + foreach(var column in Model.QueryFields.Where(u => u.QueryType != "like")) { + if (column.NetType.TrimEnd('?') == "string") { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName) == input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?') == "int" || column.NetType.TrimEnd('?') == "long") { + @:.WhereIF(input.@(column.PropertyName) != null, u => u.@(column.PropertyName) @(column.QueryType) input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?').EndsWith("Enum") || column.NetType.TrimEnd('?').EndsWith("bool")) { + @:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?') == "DateTime" && column.QueryType == "~") { + @:.WhereIF(input.@(column.PropertyName)Range?.Length == 2, u => u.@(column.PropertyName) >= input.@(column.PropertyName)Range[0] && u.@(column.PropertyName) <= input.@(column.PropertyName)Range[1]) + } + } + // 联表 + if (Model.HasJoinTable) { + foreach (var column in Model.TableFields.Where(u => (u.IsForeignKey || u.IsTree) && !u.Multiple)) { + joinTableName += ", " + column.LeftJoinAliasName; + @:.LeftJoin<@(column.JoinConfig?.EntityName)>((@joinTableName) => u.@(column.PropertyName) == @(column.LeftJoinAliasName).@(column.JoinConfig?.LinkPropertyName)) + } + // 查询列表 + @:.Select((@joinTableName) => new Page@(Model.ClassName)Output + @:{ + foreach (var column in Model.TableFields) { + @:@(column.PropertyName) = u.@(column.PropertyName), + if (column.IsForeignKey || column.IsTree) { + if (column.Multiple) { + @:@(column.LeftJoinPropertyName) = SqlFunc.Subqueryable<@(column.JoinConfig?.EntityName)>().Where(@(column.LeftJoinAliasName) => u.@(column.PropertyName).Contains(@(column.LeftJoinAliasName).@(column.JoinConfig?.LinkPropertyName).ToString())).SelectStringJoin(@(column.LeftJoinAliasName) => @(column.LeftJoinAliasName).@(column.JoinConfig.DisplayPropertyNames.Split(",").First()), ","), + } else { + @:@(column.LeftJoinPropertyName) = @(column.DisplayPropertyNames), + } } + } + @:}, true); + } else { + // 无联表 + @:.Select(); + } + @:query = query.MergeTable(); + if (Model.TableFields.Any(u => u.IsUpload)) { + @:query = query.Mapper(u => { + foreach (var column in Model.TableFields.Where(u => u.IsUpload)) { + @:if (u.@(column.PropertyName) > 0) u.@(column.LeftJoinPropertyName) = _db.Queryable().First(w => w.Id == u.@(column.PropertyName)@(column.NetType.Last() == '?' ? "!.Value" : "")); + } + @:}); + } +} + return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); } - private TypeAdapterConfig _typeAdapterConfig = TypeAdapterConfig.GlobalSettings; - public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep - ,SysCacheService sysCacheService - , UserManager userManager - ,IEventPublisher eventPublisher - @foreach (var column in Model.TableField){ - if(@column.EffectType == "ForeignKey"||@column.EffectType == "ApiTreeSelector"){ - if(@column.FkEntityName != @Model.ClassName){ - @:,SqlSugarRepository<@(@column.FkEntityName)> @(@column.LowerFkEntityName)Rep - } + /// + /// 获取@(Model.BusName)列表 🔖 + /// + /// + [DisplayName("获取@(Model.BusName)列表")] + [ApiDescriptionSettings(Name = "List"), HttpGet] + public async Task> GetList([FromQuery] Page@(@Model.ClassName)Input input) { + return await _@(Model.LowerClassName)Rep.AsQueryable() + @foreach(var column in Model.QueryFields.Where(u => u.QueryType == "like")) { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)?.Trim()), u => u.@(column.PropertyName).Contains(input.@(column.PropertyName).Trim())) + } + @foreach(var column in Model.QueryFields.Where(u => u.QueryType != "like")) { + if (column.NetType.TrimEnd('?') == "string") { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)?.Trim()), u => u.@(column.PropertyName) == input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?') == "int" || column.NetType.TrimEnd('?') == "long") { + @:.WhereIF(input.@(column.PropertyName) != null, u => u.@(column.PropertyName) @(column.QueryType) input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?').EndsWith("Enum") || column.NetType.TrimEnd('?').EndsWith("bool")) { + @:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName)) + } else if (column.NetType.TrimEnd('?') == "DateTime" && column.QueryType == "~") { + @:.WhereIF(input.@(column.PropertyName)Range?.Length == 2, u => u.@(column.PropertyName) >= input.@(column.PropertyName)Range[0] && u.@(column.PropertyName) <= input.@(column.PropertyName)Range[1]) + } + } + .ToListAsync(); + } + + /// + /// 获取@(Model.BusName)详情 ℹ️ + /// + /// + /// + [DisplayName("获取@(Model.BusName)详情")] + [ApiDescriptionSettings(Name = "Detail"), HttpGet] + public async Task Detail([FromQuery] QueryById@(Model.ClassName)Input input) + { + var query = _@(Model.LowerClassName)Rep.AsQueryable() +@{ + joinTableName = "u"; + // 联表 + if (Model.HasJoinTable) { + foreach (var column in Model.TableFields.Where(u => (u.IsForeignKey || u.IsTree) && !u.Multiple)) { + joinTableName += ", " + column.LeftJoinAliasName; + @:.LeftJoin<@(column.JoinConfig?.EntityName)>((@joinTableName) => u.@(column.PropertyName) == @(column.LeftJoinAliasName).@(column.JoinConfig?.LinkPropertyName)) + } + // 查询列表 + @:.Select((@joinTableName) => new Detail@(@Model.ClassName)Output + @:{ + foreach (var column in Model.TableFields) { + @:@(column.PropertyName) = u.@(column.PropertyName), + if (column.IsForeignKey || column.IsTree) { + if (column.Multiple) { + @:@(column.LeftJoinPropertyName) = SqlFunc.Subqueryable<@(column.JoinConfig?.EntityName)>().Where(@(column.LeftJoinAliasName) => u.@(column.PropertyName).Contains(@(column.LeftJoinAliasName).@(column.JoinConfig?.LinkPropertyName).ToString())).SelectStringJoin(@(column.LeftJoinAliasName) => @(column.LeftJoinAliasName).@(column.JoinConfig.DisplayPropertyNames.Split(",").First()), ","), + } else { + @:@(column.LeftJoinPropertyName) = @(column.DisplayPropertyNames), + } } - } - - ) - { - _@(@Model.LowerClassName)Rep = @(@Model.LowerClassName)Rep; - @foreach (var column in Model.TableField){ - if(@column.EffectType == "ForeignKey"||@column.EffectType == "ApiTreeSelector"){ - if(@column.FkEntityName != @Model.ClassName){ - @:_@(@column.LowerFkEntityName)Rep = @(@column.LowerFkEntityName)Rep; - } - } - } - _typeAdapterConfig.ForType().IgnoreNullValues(true); - _sysCacheService = sysCacheService; - _eventPublisher = eventPublisher; - _userManager = userManager; - } - - /// - /// 分页查询@(@Model.BusName) - /// - /// - /// - [ApiDescriptionSettings(Name = "Page", Description = "分页查询", Order = 1000), HttpPost] - [DisplayName("分页查询@(@Model.BusName)")] - public async Task> Page(Page@(@Model.ClassName)Input input) - { - return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).Select().OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); - } - - /// - /// 增加@(@Model.BusName) - /// - /// - /// - [ApiDescriptionSettings(Name = "Add", Description = "增加@(@Model.BusName)", Order = 990), HttpPost] - [DisplayName("增加@(@Model.BusName)")] - public async Task Add(Add@(@Model.ClassName)Input input) - { - var entity = input.Adapt<@(@Model.ClassName)>(_typeAdapterConfig); - @if(Model.RemoteVerify){ - @://验证重复值 - @:if (await _@(@Model.LowerClassName)Rep.IsAnyAsync(t => t.@(RemoteField) == entity.@(RemoteField))) - @:{ - @://已存在 - @:throw Oops.Oh(ErrorCodeEnum.D1006); - @:} + } + @:}, true); + } else { + // 无联表 + @:.Select(); } - await _@(@Model.LowerClassName)Rep.InsertAsync(entity); - return entity.@(@PKName); - } - - /// - /// 删除@(@Model.BusName) - /// - /// - /// - [ApiDescriptionSettings(Name = "Delete", Description = "删除(@Model.BusName)", Order = 980), HttpPost] - [DisplayName("删除@(@Model.BusName)")] - public async Task Delete(Delete@(@Model.ClassName)Input input) - { -@foreach (var column in Model.TableField){ -if (@column.ColumnKey == "True"){ - @:var entity = await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002); } -} - - //HJ:通过模板判断是否存在IsDelete字段,决定删除方式 - @if (Model.TableField.Any(c => c.PropertyName == "IsDelete")) - { - @:await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); // 假删除(存在IsDelete字段) - } - else - { - @:await _@(Model.LowerClassName)Rep.DeleteAsync(entity); // 真删除(不存在IsDelete字段) - } + return await query.FirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")); } /// - /// 更新@(@Model.BusName) + /// 增加@(Model.BusName) ➕ /// /// /// - [ApiDescriptionSettings(Name = "Update", Description = "更新@(@Model.BusName)", Order = 970), HttpPost] - [DisplayName("更新@(@Model.BusName)")] - public async Task Update(Update@(@Model.ClassName)Input input) + [DisplayName("增加@(Model.BusName)")] + [ApiDescriptionSettings(Name = "Add"), HttpPost] + public async Task<@(Model.PrimaryFields.First().NetType)> Add(Add@(Model.ClassName)Input input) { - var entity = input.Adapt<@(@Model.ClassName)>(_typeAdapterConfig); - entity.Id = input.Id; - @if(Model.RemoteVerify){ - @://验证重复值 - @:if (await _@(@Model.LowerClassName)Rep.IsAnyAsync(t => t.@(RemoteField) == entity.@(RemoteField) && t.@(@PKName) != entity.@(@PKName))) - @:{ - @://已存在 - @:throw Oops.Oh(ErrorCodeEnum.D1006); - @:} + var entity = input.Adapt<@(Model.ClassName)>(); + + @foreach (var column in Model.AllFields.Where(u => u.IsUnique)) { + @:if (@(column.IsRequired ? "" : $"input.{column.PropertyName} != null && ")await _@(Model.LowerClassName)Rep.IsAnyAsync(u => u.@(column.PropertyName) == input.@(column.PropertyName))) throw Oops.Oh("@(column.ColumnComment)已存在"); } - await _@(@Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); + + return await _@(Model.LowerClassName)Rep.InsertAsync(entity) ? entity.@(Model.PrimaryFields.First().PropertyName) : default; } /// - /// 获取@(@Model.BusName)详情 + /// 更新@(Model.BusName) ✏️ /// /// /// - [ApiDescriptionSettings(Name = "Detail", Description = "获取@(@Model.BusName)", Order = 960), HttpGet] - [DisplayName("获取@(@Model.BusName)")] - public async Task<@(@Model.ClassName)> Detail([FromQuery] QueryById@(@Model.ClassName)Input input) + [DisplayName("更新@(Model.BusName)")] + [ApiDescriptionSettings(Name = "Update"), HttpPost] + public async Task Update(Update@(Model.ClassName)Input input) { -@foreach (var column in Model.TableField){ -if (@column.ColumnKey == "True"){ - @:return await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)); -} -} + @foreach (var column in Model.AllFields.Where(u => u.IsUnique)) { + var preWhere = column.IsRequired ? "" : $"input.{column.PropertyName} != null && "; + var preWhere2 = Model.PrimaryKeysFormat(" && ", "u.{0} != input.{0}"); + @:if (@(preWhere)await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @preWhere2 && u.@(column.PropertyName) == input.@(column.PropertyName))) throw Oops.Oh("@(column.ColumnComment)已存在"); + } + var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")) ?? throw Oops.Oh(ErrorCodeEnum.D1002); + entity.Copy(input); + await _@(Model.LowerClassName)Rep.AsUpdateable(entity).ExecuteCommandAsync(); } /// - /// 导入@(@Model.BusName) - /// - /// - /// - [DisplayName("导入@(@Model.BusName)")] - public async Task Import([Required] IFormFile file) - { - using MemoryStream stream = new(); - await file.CopyToAsync(stream); - - var res = await new ExcelImporter().Import<@(@Model.ClassName)Dto>(stream); - - if (res == null || res.Exception != null) - throw Oops.Oh(res.Exception); - - var importData = res.Data.ToList(); - // 按照编码条件进行批量更新或者新增 - await _@(@Model.LowerClassName)Rep.Context.Storageable(importData.Adapt>()).WhereColumns(u => u.Id).ExecuteCommandAsync(); - } - - /// - /// 导出@(@Model.BusName) - /// - /// - [DisplayName("导出@(@Model.BusName)")] - public async Task Export(Page@(@Model.ClassName)Input input) - { - - var list = await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).Select<@(@Model.ClassName)Dto>().OrderBuilder(input).ToListAsync(); - - if (list == null || list.Count < 1) - throw Oops.Oh("数据为空,导出已取消"); - - var res = await new ExcelExporter().ExportAsByteArray(list); - return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "@(@Model.BusName)列表.xlsx" }; - } - - /// - /// 下载导入@(@Model.BusName)模板 - /// - /// - [DisplayName("下载导入@(@Model.BusName)模板")] - public async Task DownloadTemplate() - { - var res = await new ExcelImporter().GenerateTemplateBytes<@(@Model.ClassName)Dto>(); - return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = "@(@Model.BusName)导入模板.xlsx" }; - } - - /// - /// 获取@(@Model.BusName)列表 + /// 删除@(Model.BusName) ❌ /// /// /// - [ApiDescriptionSettings(Name = "List", Description = "获取@(@Model.BusName)列表", Order = 950), HttpPost] - [DisplayName("获取@(@Model.BusName)列表")] - public async Task> List(Page@(@Model.ClassName)Input input) + [DisplayName("删除@(Model.BusName)")] + [ApiDescriptionSettings(Name = "Delete"), HttpPost] + public async Task Delete(Primarykey@(Model.ClassName)Input input) { - return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).Select().OrderBuilder(input).ToListAsync(); + var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")) ?? throw Oops.Oh(ErrorCodeEnum.D1002); + @if (Model.AllFields.Any(u => u.PropertyName == "IsDelete")) { + @:await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); // 假删除 + // await _@(Model.LowerClassName)Rep.DeleteAsync(entity); //真删除 + } else { + // await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); // 假删除 + @:await _@(Model.LowerClassName)Rep.DeleteAsync(entity); //真删除 + } } - /*HJ:不要这应该在视图里,或者用ReZero里用 /// - /// 获取@(@Model.BusName) + /// 批量删除@(Model.BusName) ❌ /// /// /// - [ApiDescriptionSettings(Name = "GetTotalSum", Description = "获取@(@Model.BusName)统计", Order = 960), HttpPost] - [DisplayName("获取@(@Model.BusName)统计")] - public async Task> GetTotalSum(Page@(@Model.ClassName)Input input) + [DisplayName("批量删除@(Model.BusName)")] + [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost] + public async Task BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<@(Model.IsOnlyIdPrimary ? Model.PrimaryFields.First().NetType : $"Delete{Model.ClassName}")> input) { - // 单次查询同时获取统计值 - var querystats = @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input) - @if(@Model.TableField.Any(x=>x.IsGroupBy == "Y")){ - @foreach (var column in Model.TableField){ - if (@column.IsGroupBy == "Y"){ - @: .GroupByIF(input.GroupBy.Contains("@column.PropertyName"), u => u.@column.PropertyName) - } + @if (Model.IsOnlyIdPrimary) { + @:var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(u => input.Contains(u.Id)).ToListAsync(); + } else { + @:var exp = Expressionable.Create<@(Model.ClassName)>(); + @:foreach (var row in input) exp = exp.Or(it => @Model.PrimaryKeysFormat(" && ", "it.{0} == row.{0}")); + @:var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(exp.ToExpression()).ToListAsync(); } - @: //.Having(it => SqlFunc.AggregateCount(it.Id) > 0)//聚合函数过滤 - } - .Select(it => new @(@Model.ClassName)Output - { - // count = SqlFunc.AggregateCount(it.Id), - @foreach (var column in Model.TableField){ - if (@column.IsGroupBy == "Y"){ - @: @(@column.PropertyName) = it.@(@column.PropertyName), - } - if (@column.Statistical == "Y"){ - @: @(@column.PropertyName) = SqlFunc.AggregateSum(it.@(@column.PropertyName)) ?? 0, - } - } - }); - return await querystats.ToListAsync(); - } - */ - - /*HJ:不要危险度大,容易SQL注入,且不符合规范 - /// - /// 根据输入参数获取@(@Model.BusName)统计 - /// 支持双模式聚合配置: - /// 常规模式:Field + Function - /// 高级模式:CustomExpression(支持任意合法SQL表达式) - /// 智能条件组合: - /// 多个HAVING条件自动用AND连接 - /// 条件表达式自动包裹聚合函数(如SUM(cost) > 10000) - /// 可扩展支持OR条件 - /// - /// - /// - [ApiDescriptionSettings(Name = "GetAggregTotalSum", Order = 970), HttpPost] - [DisplayName("根据输入参数获取@(@Model.BusName)统计")] - public async Task> GetAggregTotalSum(Page@(@Model.ClassName)Input input) - { - - var query = @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input); - // 输入参数示例 - //input = new Page@(@Model.ClassName)Input - //{ - // GroupBy = ["department", "project"], - // GroupBy = input.GroupBy, - // Aggregations = - // [ - // new AggregationConfig - //{ - // Field = "cost", - // Function = AggregateFunction.Sum, - // Alias = "totalCost", - // HavingCondition = "> 10000" - //}, - //new AggregationConfig - //{ - // CustomExpression = "AVG(CAST(response_time AS FLOAT))", - // Alias = "avgResponse", - // HavingCondition = "< 500" - //} - // ] - //}; - // 生成SQL示例 - // SELECT - // department, project, - // SUM(cost) AS totalCost, - // AVG(CAST(response_time AS FLOAT)) AS avgResponse - // FROM... - // GROUP BY department, project - // HAVING(SUM(cost) > 10000) AND(AVG(CAST(response_time AS FLOAT)) < 500) - @if(@Model.TableField.Any(x=>x.Statistical == "Y")){ - - @:// 处理分组字段 - @:var groupFields = AggregationBuilder.ValidateFields<@(@Model.ClassName)Output>(input.GroupBy, typeof(@(@Model.ClassName)Output)); - @:if (groupFields.Count > 0) - @:{ - @: query = query.GroupBy(string.Join(",", groupFields)); - @:} - @:// 构建聚合配置 - @:var aggregator = new AggregationBuilder( - @: configs: input.Aggregations, - @: entityType: typeof(@(@Model.ClassName)), - @: outputType: typeof(@(@Model.ClassName)Output) - @:); - - @:// 组合SELECT语句 - @:var selectParts = groupFields.Select(f => $"{f} AS {f}") - @: .Concat(aggregator.SelectParts) - @: .ToList(); - - @:// 应用HAVING条件 - @:if (aggregator.HavingConditions.Count > 0) - @:{ - @: query = query.Having(string.Join(" AND ", aggregator.HavingConditions)); - @:} - - @:// 执行查询 - @:return await query.Select<@(@Model.ClassName)Output>(string.Join(", ", selectParts)).ToListAsync(); - } - else - { - @:return await query.ToListAsync(); + @if (Model.AllFields.Any(u => u.PropertyName == "IsDelete")) { + @:return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list); // 假删除 + //return await _@(Model.LowerClassName)Rep.DeleteAsync(list); //真删除 + } else { + //return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list) ? list.Count : 0; // 假删除 + @:return await _@(Model.LowerClassName)Rep.DeleteAsync(list) ? list.Count : 0; //真删除 } } - */ - -@foreach (var column in Model.TableField){ -if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ +@if (Model.HasStatus) { + @: @:/// - @:/// 获取@(@column.ColumnComment)列表 - @:/// - @:/// - @:[ApiDescriptionSettings(Name = "@(LowerFirstLetter(@column.FkEntityName))@(@column.PropertyName)Dropdown", Description = "获取@(@column.ColumnComment)列表", Order = 940), HttpGet] - @:[DisplayName("获取@(@column.ColumnComment)列表")] - @:public async Task> @(@column.FkEntityName)@(@column.PropertyName)Dropdown() - @:{ - @:return (await _@(@column.LowerFkEntityName)Rep.GetListAsync(t => t.Id > 0)) - @:.Select(u => new LabelValueOutput - @:{ - @:Label = u.@(@column.FkColumnName), - @:Value = u.@(@column.FkLinkColumnName).ToString() - @:} - @:).ToList(); - @:} -} -} - -@foreach (var column in Model.TableField){ -if(@column.EffectType == "Upload"){ - @:/// - @:/// 上传@(@column.ColumnComment) - @:/// - @:/// - @:/// - @:[ApiDescriptionSettings(Name = "Upload@(@column.PropertyName)", Description = "上传@(@column.ColumnComment)", Order = 930), HttpPost] - @:[DisplayName("上传@(@column.ColumnComment)")] - @:public async Task Upload@(@column.PropertyName)([Required] IFormFile file) - @:{ - @:var service = App.GetRequiredService(); - @:return await service.UploadFile(new UploadFileInput { File = file}, "upload/@(@column.PropertyName)" ); - @:} -} -} - -@foreach (var column in Model.TableField){ - -if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){ - @:/// - @:/// 获取@(@Model.BusName)树列表 + @:/// 设置@(Model.BusName)状态 🚫 @:/// @:/// @:/// - @:[ApiDescriptionSettings(Name = "Tree", Description = "获取@(@Model.BusName)树列表", Order = 960), HttpPost] - @:[DisplayName("获取@(@Model.BusName)树列表")] - @:public async Task Tree(Page@(@Model.ClassName)Input input) + @:[DisplayName("设置@(Model.BusName)状态")] + @:[ApiDescriptionSettings(Name = "SetStatus"), HttpPost] + @:public async Task SetStatus(Set@(Model.ClassName)StatusInput input) @:{ - - @:// 有筛选条件时返回list列表(防止构造不出树) - @:var isSearch=false; - - foreach (var columnTree in Model.TableField){ - if (@columnTree.QueryWhether == "Y"){ - if (@columnTree.NetType?.TrimEnd('?') == "string"){ - @:if(!string.IsNullOrWhiteSpace(input.@columnTree.PropertyName))isSearch=true; - - }else if((@columnTree.NetType?.TrimEnd('?') == "int" || @columnTree.NetType?.TrimEnd('?') == "long")){ - @:if(input.@columnTree.PropertyName>0)isSearch=true; - - }else if((@columnTree.NetType?.TrimEnd('?') == "DateTime" && @columnTree.QueryType == "~")){ - @:if(input.@(@columnTree.PropertyName)Range != null)isSearch=true; - } }} - - - @:if (isSearch||!string.IsNullOrEmpty(input.SearchKey?.Trim())) - @:{ - @:// 有筛选条件时返回列表 - @:return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).Distinct().ToListAsync(); - @:} - - @:// 无筛选条件时返回树列表 - @:return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).OrderBuilder(input).ToTreeAsync(u => u.Children, u => u.Pid, 0, u => u.Id); + @:await _@(Model.LowerClassName)Rep.AsUpdateable().SetColumns(u => u.Status, input.Status).Where(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}")).ExecuteCommandAsync(); @:} } -} -@if(Model.RemoteVerify){ +@foreach (var column in Model.UploadFields) { @:/// - @:/// 检查@(RemoteField)字段是否可用 + @:/// 上传@(column.ColumnComment) ⬆️ @:/// - @:/// 检查字段参数 + @:/// @:/// - @:[ApiDescriptionSettings(Name = "exists@(RemoteField)",Description = "检查@(RemoteField)字段是否可用", Order = 910), HttpPost] - @:[DisplayName("检查@(RemoteField)字段是否可用")] - @:public async Task Exists@(RemoteField)Async(Exists@(RemoteField)Input param) + @:[DisplayName("上传@(column.ColumnComment)")] + @:[ApiDescriptionSettings(Name = "Upload@(column.PropertyName)"), HttpPost] + @:public async Task Upload@(column.PropertyName)([Required] IFormFile file) @:{ - @:if (string.IsNullOrWhiteSpace(param.FieldValue)) - @:{ - @:return false; - @:} - @:if (!string.IsNullOrWhiteSpace(param.OldFieldValue)) - @:{ - @:if (param.FieldValue.Trim() == param.OldFieldValue.Trim()) - @:{ - @://编辑状态下触发的 - @:return true; - @:} - @:} + @:return await _sysFileService.UploadFile(new UploadFileInput { File = file, SavePath = "upload/@(Model.ClassName)/@(column.PropertyName)" }); + @:} + @: + } - @:return !(await _@(@Model.LowerClassName)Rep.IsAnyAsync(t => t.@(RemoteField).Equals(param.FieldValue.Trim()))); +@foreach (var column in Model.ApiFields.GroupBy(u => new { u.IsTree, u.JoinConfig.EntityName }).Select(u => u.First()).ToList()) { + var extarName = column.IsTree ? "Tree" : "Page"; + var methodName = column.JoinConfig.EntityName + extarName; + @:/// + @:/// 获取@(column.JoinConfig.TableComment)数据 ⬆️ + @:/// + @:/// + @:/// + @:[DisplayName("获取@(column.JoinConfig.TableComment)数据")] + @:[ApiDescriptionSettings(Name = "@(methodName)"), HttpPost] + @:public async Task<@(column.IsTree ? "List" : "SqlSugarPagedList")<@(column.JoinConfig.EntityName)>> @(methodName)(@(extarName + Model.ClassName + column.JoinConfig.EntityName)Input input) + @:{ + @:return await _db.Queryable<@(column.JoinConfig.EntityName)>() + if (!string.IsNullOrWhiteSpace(column.JoinConfig.SearchPropertyName)) { + @:.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.@(column.JoinConfig.SearchPropertyName).Contains(input.Keyword)) + } + if (column.IsTree) { + if (string.IsNullOrWhiteSpace(column.JoinConfig.ParentPropertyName)) { + @:.ToListAsync(); + } else { + @:.ToTreeAsync(u => u.Children, u => u.@(column.JoinConfig.ParentPropertyName), 0); + } + } else { + @:.InnerJoinIF<@Model.ClassName>(!input.All, (u, r) => @(column.Multiple ? $"r.{column.PropertyName}.Contains(u.{column.JoinConfig.LinkPropertyName}.ToString())" : $"u.{column.JoinConfig.LinkPropertyName} == r.{column.PropertyName}")) + @:.Select(u => u).MergeTable().OrderBuilder(input) + @:.ToPagedListAsync(input.Page, input.PageSize); + } + @:} + @: +} + +@if (Model.ImportFields.Count > 0) { + @:/// + @:/// 导出@(Model.BusName)记录 🔖 + @:/// + @:/// + @:/// + @:[DisplayName("导出@(Model.BusName)记录")] + @:[ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify] + @:public async Task Export(Export@(@Model.ClassName)Input input) + @:{ + @:var list = (await Page(input)).Items?.Adapt>() ?? new(); + @:if (input.SelectKeyList?.Count > 0) list = list.Where(x => input.SelectKeyList.Contains(x.@(Model.PrimaryFields.First().PropertyName))).ToList(); + var dictFields = Model.TableFields.Where(x => x.IsImport && x.IsDict) ?? default; + foreach (var column in dictFields) { + @:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictConfig.Code)" }).Result.ToDictionary(x => x.Value, x => x.Label); + } + if (dictFields.Count() > 0) { + @:list.ForEach(e => { + foreach (var column in dictFields) { + @:e.@(column.PropertyName + "Label") = @(column.LowerPropertyName)DictMap.GetValueOrDefault(e.@(column.PropertyName) ?? "", e.@(column.PropertyName)); + } + @:}); + } + @:return ExcelHelper.ExportTemplate(list, "@(Model.BusName)导出记录"); + @:} + @: + @:/// + @:/// 下载@(Model.BusName)数据导入模板 ⬇️ + @:/// + @:/// + @:[DisplayName("下载@(Model.BusName)数据导入模板")] + @:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify] + @:public IActionResult DownloadTemplate() + @:{ + var fieldsList = Model.ImportFields.Where(u => u.IsForeignKey || u.IsTree).ToList(); + if (fieldsList.Any()) { + @:return ExcelHelper.ExportTemplate(new List(), "@(Model.BusName)导入模板", (_, info) => + @:{ + @:// 自定义指定列下拉框数据源 + foreach (var column in fieldsList) { + var columnList = column.JoinConfig.DisplayPropertyNames.Split(",").Select(n => $"{{u.{n}}}").ToList(); + @:if (nameof(Export@(Model.ClassName)Output.@(column.PropertyName + "Label")) == info.Name) return _db.Queryable<@(column.JoinConfig.EntityName)>().Select(u => $"@(string.Join("-", columnList))").Distinct().ToList(); + } + @:return null; + @:}); + } else { + @:return ExcelHelper.ExportTemplate(new List(), "@(Model.BusName)导入模板"); + } + @:} + @: + @:/// + @:/// 导入@(Model.BusName)记录 💾 + @:/// + @:/// + @:[DisplayName("导入@(Model.BusName)记录")] + @:[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork] + @:public IActionResult ImportData([Required] IFormFile file) + @:{ + @:lock (this) + @:{ + var dictTableField = Model.TableFields.Where(x => x.IsImport && x.IsDict) ?? default; + foreach (var column in dictTableField) { + @:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictConfig.Code)" }).Result.ToDictionary(x => x.Label!, x => x.Value); + } + + @:var stream = ExcelHelper.ImportData(file, (list, markerErrorAction) => + @:{ + @:_db.Utilities.PageEach(list, 1024, pageItems => + @:{ + foreach (var column in Model.ImportFields.Where(u => u.IsForeignKey || u.IsTree)) { + @:// 链接 @(column.ColumnComment) + @:var @(column.LowerPropertyName)LabelList = pageItems.Where(x => x.@(column.PropertyName + "Label") != null).Select(x => x.@(column.PropertyName + "Label")).Distinct().ToList(); + @:if (@(column.LowerPropertyName)LabelList.Any()) { + var columnList = column.JoinConfig.DisplayPropertyNames.Split(",").Select(n => $"{{u.{n}}}").ToList(); + @:var @(column.LowerPropertyName)LinkMap = _db.Queryable<@(column.JoinConfig.EntityName)>().Where(u => @(column.LowerPropertyName)LabelList.Contains($"@(string.Join("-", columnList))")).ToList().ToDictionary(u => $"@(string.Join("-", columnList))", u => u.@(column.JoinConfig.LinkPropertyName)); + @:pageItems.ForEach(e => { + if (column.Multiple) { + @:e.@(column.PropertyName) = e.@(column.PropertyName + "Label").Split(",").Select(x => @(column.LowerPropertyName)LinkMap.GetValueOrDefault(x)).Join(); + } else { + @:e.@(column.PropertyName) = @(column.LowerPropertyName)LinkMap.GetValueOrDefault(e.@(column.PropertyName + "Label") ?? ""); + } + @:if (e.@(column.PropertyName) == null) e.Error = "@(column.ColumnComment)链接失败"; + @:}); + @:} + } + + if (dictTableField.Any()) { + @: + @:// 映射字典值 + @:foreach(var item in pageItems) { + foreach (var column in dictTableField) { + @:if (string.IsNullOrWhiteSpace(item.@(column.PropertyName + "Label"))) continue; + @:item.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(item.@(column.PropertyName + "Label")); + @:if (item.@(column.PropertyName) == null) item.Error = "@(column.ColumnComment)字典映射失败"; + } + @:} + } + + @: + @:// 校验并过滤必填基本类型为null的字段 + @:var rows = pageItems.Where(x => { + foreach (var column in Model.ImportFields.Where(x => x.IsRequired && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){ + @:if (!string.IsNullOrWhiteSpace(x.Error)) return false; + @:if (x.@(column.PropertyName) == null) { + @:x.Error = "@(column.ColumnComment)不能为空"; + @:return false; + @:} + } + @:return true; + @:}).Adapt>(); + + @: + @:var storageable = _@(Model.LowerClassName)Rep.Context.Storageable(rows) + foreach (var column in Model.ImportFields) { + if (column.IsRequired) { + if(column.NetType.TrimEnd('?') == "string") { + @:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(column.PropertyName)), "@(column.ColumnComment)不能为空") + } else if(column.NetType.EndsWith('?') == true) { + @:.SplitError(it => it.Item.@(column.PropertyName) == null, "@(column.ColumnComment)不能为空") + }} + if (column.NetType?.TrimEnd('?') == "string" && column.ColumnLength > 0) { + @:.SplitError(it => it.Item.@(column.PropertyName)?.Length > @(column.ColumnLength), "@(column.ColumnComment)长度不能超过@(column.ColumnLength)个字符") + } + if (column.IsUnique) { + @:.WhereColumns(it => it.@(column.PropertyName)).SplitError(it => it.Any(), "@(column.ColumnComment)已存在") + }} + @:.SplitInsert(_ => true) + @:.ToStorage(); + + @: + @:storageable.BulkCopy(); + @:storageable.BulkUpdate(); + @: + @:// 标记错误信息 + @:markerErrorAction.Invoke(storageable, pageItems, rows); + @:}); + @:}); + @: + @:return stream; + @:} @:} } - -} - -@{ -string LowerFirstLetter(string text) -{ -return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 -} -} +} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/sys_menu_seed_data.cs.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/sys_menu_seed_data.cs.vm deleted file mode 100644 index da545a6b..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/sys_menu_seed_data.cs.vm +++ /dev/null @@ -1,71 +0,0 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -@{ - long lastId=0; - long pid=0; - int zindex=0; - int orderNo=0; - string createTime=""; -} - -namespace @(@Model.NameSpace).SeedData; - -/// -/// @(@Model.BusName)系统菜单表种子数据 -/// -// [IncreSeed] -public class @(@Model.ClassName)MenuSeedData : ISqlSugarEntitySeedData -{ - /// - /// 种子数据 - /// - /// - public IEnumerable HasData() - { - //菜单列表 - var list = new List(); - - #region @(@Model.BusName) -@foreach (var menu in Model.MenuList){ -@{ -zindex=zindex+1; -} -@if((int)menu.Type==1){ - @:list.Add(new SysMenu { Id = @(menu.Id), Pid = @(menu.Pid), Title = "@(menu.Title)", Path = "@(menu.Path)", Name = "@(menu.Name)", Component = "Layout", Icon = "@(menu.Icon)", Type = MenuTypeEnum.Dir, CreateTime = DateTime.Parse("@(menu.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"))"), OrderNo = @(menu.OrderNo) }); -} -@if((int)menu.Type==2){ - @{ - pid=menu.Id; - } - @:list.Add(new SysMenu { Id = @(menu.Id), Pid = @(menu.Pid), Title = "@(menu.Title)", Path = "@(menu.Path)", Name = "@(menu.Name)", Component = "@(menu.Component)", Icon = "@(menu.Icon)", Type = MenuTypeEnum.Menu, CreateTime = DateTime.Parse("@(menu.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"))"), OrderNo = @(menu.OrderNo) }); -} -@if((int)menu.Type==3) { - @:list.Add(new SysMenu { Id = @(menu.Id), Pid = @(menu.Pid), Title = "@(menu.Title)", Permission = "@(menu.Permission)", Type = MenuTypeEnum.Btn, CreateTime = DateTime.Parse("@(menu.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"))"), OrderNo = @(menu.OrderNo) }); -} -@if(zindex==Model.MenuList.Count){ - lastId=menu.Id-18888; - orderNo=menu.OrderNo; - createTime=menu.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"); -} -} -@if(@Model.PrintType != "off"){ - @:list.Add(new SysMenu { Id = @(lastId+1), Pid = @(pid), Title = "打印", Permission = "@(LowerFirstLetter(@Model.ClassName))/print", Type = MenuTypeEnum.Btn, CreateTime = DateTime.Parse("@(createTime)"), OrderNo = @(orderNo+10) }); -} - // list.Add(new SysMenu { Id = @(lastId+2), Pid = @(pid), Title = "导入", Permission = "@(LowerFirstLetter(@Model.ClassName))/import", Type = MenuTypeEnum.Btn, CreateTime = DateTime.Parse("@(createTime)"), OrderNo = @(orderNo+20) }); - // list.Add(new SysMenu { Id = @(lastId+3), Pid = @(pid), Title = "导出", Permission = "@(LowerFirstLetter(@Model.ClassName))/export", Type = MenuTypeEnum.Btn, CreateTime = DateTime.Parse("@(createTime)"), OrderNo = @(orderNo+30) }); - #endregion - - return list; - } -} - -@{ -string LowerFirstLetter(string text) -{ -return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 -} -} \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_api.ts.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_api.ts.vm index 48210737..6b65474b 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_api.ts.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_api.ts.vm @@ -1,177 +1,63 @@ +//////////////////////////////////////////////////////////////////// +// 作者:@(Model.AuthorName ?? "喵你个汪") +// 时间:@(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) +// 邮箱:@(Model.Email ?? "Admin.NET@qq.com") +//////////////////////////////////////////////////////////////////// + +import {useBaseApi} from '/@@/api/base'; + +// @(Model.BusName)接口服务 +export const use@(Model.ClassName)Api = () => { + const baseApi = useBaseApi("@(Model.LowerClassName)"); + return { + // 分页查询@(Model.BusName) + page: baseApi.page, + // 查看@(Model.BusName)列表 + list: baseApi.list, + // 查看@(Model.BusName)详细 + detail: baseApi.detail, + // 新增@(Model.BusName) + add: baseApi.add, + // 更新@(Model.BusName) + update: baseApi.update, + @if (Model.HasStatus) { + @:// 设置@(Model.BusName)状态 + @:setStatus: baseApi.setStatus, + } + // 删除@(Model.BusName) + delete: baseApi.delete, + // 批量删除@(Model.BusName) + batchDelete: baseApi.batchDelete, + @if (Model.ImportFields.Count > 0) { + @:// 导出@(Model.BusName)数据 + @:exportData: baseApi.exportData, + @:// 导入@(Model.BusName)数据 + @:importData: baseApi.importData, + @:// 下载@(Model.BusName)数据导入模板 + @:downloadTemplate: baseApi.downloadTemplate, + } + @foreach (var column in Model.ApiFields.GroupBy(u => new { u.IsTree, u.JoinConfig.EntityName }).Select(u => u.First()).ToList()) { + var methodName = column.JoinConfig.EntityName + (column.IsTree ? "Tree" : "Page"); + @:// 获取@(column.JoinConfig.TableComment)数据 + @:get@(methodName): (data: any, cancel: boolean = false) => baseApi.request({ + @: url: baseApi.baseUrl + '@(Model.GetFirstLower(methodName))', + @: method: 'post', + @: data + @:}, cancel), + } + @foreach (var column in Model.UploadFields) { + @:// 上传@(column.ColumnComment) + @:upload@(column.PropertyName): (params: any, cancel: boolean = false) => baseApi.uploadFile(params, 'upload@(column.PropertyName)', cancel), + } + } +} + +// @(Model.BusName)实体 +export interface @(Model.ClassName) { @{ - Dictionary definedObjects = new Dictionary(); - string RemoteField=""; - string PKName=""; - foreach (var column in Model.TableField){ - if(column.RemoteVerify){ - RemoteField=@column.PropertyName; - } - if(column.ColumnKey == "True"){ - PKName=column.PropertyName; - } - } +foreach (var column in Model.AllFields) { + @:// @(column.ColumnComment) @(column.NetType) + @:@(column.LowerPropertyName)@(column.IsRequired ? "" : "?"): @(Model.GetTypeScriptType(column.NetType)); } -import request from '/@@/utils/request'; -enum Api { - Add@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/add', - Delete@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/delete', - Update@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/update', - Page@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/page', - List@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/list', - Tree@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/GetTreeList', - Get@(@Model.ClassName)TotalSum = '/api/@(@Model.LowerClassName)/GetTotalSum', - Get@(@Model.ClassName)AggregTotalSum = '/api/@(@Model.LowerClassName)/GetAggregTotalSum', - Detail@(@Model.ClassName) = '/api/@(@Model.LowerClassName)/detail', - @if(Model.RemoteVerify){ - @:Exists@(RemoteField) = '/api/@(@Model.LowerClassName)/exists@(RemoteField)', - } - @foreach (var column in Model.TableField){ -if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ - @:Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))@(@column.PropertyName)Dropdown', -}else if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("Get@(@column.FkEntityName)Tree")){ - @{definedObjects.Add("Get@(@column.FkEntityName)Tree", 1);} - @:Get@(@column.FkEntityName)Tree = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))Tree', -}else if(@column.EffectType == "Upload"){ - @:Upload@(@column.PropertyName) = '/api/@(@Model.LowerClassName)/upload@(@column.PropertyName)', -} -} -} - -// 增加@(@Model.BusName) -export const add@(@Model.ClassName) = (params?: any) => - request({ - url: Api.Add@(@Model.ClassName), - method: 'post', - data: params, - }); - -// 删除@(@Model.BusName) -export const delete@(@Model.ClassName) = (params?: any) => - request({ - url: Api.Delete@(@Model.ClassName), - method: 'post', - data: params, - }); - -// 编辑@(@Model.BusName) -export const update@(@Model.ClassName) = (params?: any) => - request({ - url: Api.Update@(@Model.ClassName), - method: 'post', - data: params, - }); - -// 分页查询@(@Model.BusName) -export const page@(@Model.ClassName) = (params?: any) => - request({ - url: Api.Page@(@Model.ClassName), - method: 'post', - data: params, - }); -// 列表查询@(@Model.BusName) -export const list@(@Model.ClassName) = (params?: any) => - request({ - url: Api.List@(@Model.ClassName), - method: 'post', - data: params, - }); -// treelist@(@Model.BusName) -export const treelist@(@Model.ClassName) = (params?: any) => - request({ - url: Api.Tree@(@Model.ClassName), - method: 'post', - data: params, - }); -// 获取统计,支持全表和分组 -export const get@(@Model.ClassName)TotalSum = (params?: any) => - request({ - url: Api.Get@(@Model.ClassName)TotalSum, - method: 'post', - data: params, - }); -// 输入参数获取统计 -export const get@(@Model.ClassName)AggregTotalSum = (params?: any) => - request({ - url: Api.Get@(@Model.ClassName)AggregTotalSum, - method: 'post', - data: params, - }); -// 详情@(@Model.BusName) -export const detail@(@Model.ClassName) = (id: any) => - request({ - url: Api.Detail@(@Model.ClassName), - method: 'get', - data: { id }, - }); - -@foreach (var column in Model.TableField){ - if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ -@:export const get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = () => - @:request({ - @:url: Api.Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown, - @:method: 'get' - @:}); -}else if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("get@(@column.FkEntityName)Tree")){ -@{definedObjects.Add("get@(@column.FkEntityName)Tree", 1);} -@:export const get@(@column.FkEntityName)Tree = () => - @:request({ - @:url: Api.Get@(@column.FkEntityName)Tree, - @:method: 'get' - @:}); -}else if(@column.EffectType == "Upload"){ - -@:// 上传@(@column.ColumnComment) -@:export const upload@(@column.PropertyName) = (params: any) => uploadFileHandle(params, Api.Upload@(@column.PropertyName)) - } -} - -@foreach (var column in Model.TableField){ -if(@column.EffectType == "Upload"){ -@:export const uploadFileHandle = (params: any, url: string) => { - @:const formData = new window.FormData(); - @:formData.append('file', params.file); - - @://自定义参数 - @:if (params.data) { - @:Object.keys(params.data).forEach((key) => { - @:const value = params.data![key]; - @:if (Array.isArray(value)) { - @:value.forEach((item) => { - @:formData.append(`${key}[]`, item); - @:}); - @:return; - @:} - @:formData.append(key, params.data![key]); - @:}); -@:} -@:return request({ - @:url: url, - @:method: 'POST', - @:data: formData, - @:headers: { - @:'Content-type': 'multipart/form-data;charset=UTF-8', - @:// ts-ignore - @:ignoreCancelToken: true, - @:}, - @:}); -@:}; - break; - } -} - -@if(Model.RemoteVerify){ -@:// 检查@(RemoteField)是否存在 -@:export const exists@(RemoteField) = (params?: any) => - @:request({ - @:url: Api.Exists@(RemoteField), - @:method: 'post', - @:data: params, - @:}); -} - -@{ -string LowerFirstLetter(string text) -{ -return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_BottomIndx.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_BottomIndx.vue.vm deleted file mode 100644 index 69d063d2..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_BottomIndx.vue.vm +++ /dev/null @@ -1,113 +0,0 @@ -@{ - string LowerFirstLetter(string text) - { - return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 - } - var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault(); - string pkFieldName = null; - if(pkField != null && !string.IsNullOrEmpty(pkField.PropertyName)) - { - pkFieldName = LowerFirstLetter(pkField.PropertyName); - } - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - } -} - - - - - diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_LeftTree.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_LeftTree.vue.vm deleted file mode 100644 index 2d32c6e1..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_LeftTree.vue.vm +++ /dev/null @@ -1,82 +0,0 @@ -@{ - string LowerFirstLetter(string text) - { - return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 - } - var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault(); - string pkFieldName = null; - if(pkField != null && !string.IsNullOrEmpty(pkField.PropertyName)) - { - pkFieldName = LowerFirstLetter(pkField.PropertyName); - } - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - } -} - - - - - diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm deleted file mode 100644 index 766d8cae..00000000 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_List.vue.vm +++ /dev/null @@ -1,657 +0,0 @@ -@{ - string LowerFirstLetter(string text) - { - return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 - } - var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault(); - string pkFieldName = null; - if(pkField != null && !string.IsNullOrEmpty(pkField.PropertyName)) - { - pkFieldName = LowerFirstLetter(pkField.PropertyName); - } - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - } -} - - - - diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_Tree.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_Tree.vue.vm index 255cb9ec..a4ad80d3 100644 --- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_Tree.vue.vm +++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/template/web_views_Tree.vue.vm @@ -1,77 +1,58 @@ -@{ - string LowerFirstLetter(string text) - { - return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写 - } - var pkField = Model.TableField.Where(c => c.ColumnKey == "True").FirstOrDefault(); - string pkFieldName = null; - if(pkField != null && !string.IsNullOrEmpty(pkField.PropertyName)) - { - pkFieldName = LowerFirstLetter(pkField.PropertyName); - } - Dictionary definedObjects = new Dictionary(); - bool haveLikeCdt = false; - foreach (var column in Model.TableField){ - if (column.QueryWhether == "Y" && column.QueryType == "like"){ - haveLikeCdt = true; - } - } -} -