XCode/XCodeTool/CubeBuilder.cs

386 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NewLife;
using NewLife.Collections;
using NewLife.Log;
using NewLife.Reflection;
using XCode.Code;
using XCode.DataAccessLayer;
namespace XCode;
/// <summary>魔方页面生成器</summary>
public class CubeBuilder : ClassBuilder
{
#region
/// <summary>区域名</summary>
public String AreaName { get; set; }
/// <summary>根命名空间</summary>
public String RootNamespace { get; set; }
/// <summary>菜单排序</summary>
public Int32 Sort { get; set; }
/// <summary>区域模版</summary>
public String AreaTemplate { get; set; } = """
using System.ComponentModel;
using NewLife;
using NewLife.Cube;
namespace {RootNamespace}.Areas.{Name};
[DisplayName("{DisplayName}")]
public class {Name}Area : AreaBase
{
public {Name}Area() : base(nameof({Name}Area).TrimEnd("Area")) { }
}
""";
/// <summary>控制器模版</summary>
public String ControllerTemplate { get; set; } = """
using Microsoft.AspNetCore.Mvc;
using {EntityNamespace};
using NewLife;
using NewLife.Cube;
using NewLife.Cube.Extensions;
using NewLife.Cube.ViewModels;
using NewLife.Log;
using NewLife.Web;
using XCode.Membership;
using static {EntityNamespace}.{EntityName};
namespace {RootNamespace}.Areas.{Name}.Controllers;
/// <summary>{DisplayName}</summary>
[Menu({Sort}, true, Icon = "fa-table")]
[{Name}Area]
public class {ClassName} : {BaseClass}<{EntityName}>
{
static {ClassName}()
{
//LogOnChange = true;
//ListFields.RemoveField("Id", "Creator");
ListFields.RemoveCreateField().RemoveRemarkField();
//{
// var df = ListFields.GetField("Code") as ListField;
// df.Url = "?code={Code}";
// df.Target = "_blank";
//}
//{
// var df = ListFields.AddListField("devices", null, "Onlines");
// df.DisplayName = "查看设备";
// df.Url = "Device?groupId={Id}";
// df.DataVisible = e => (e as {EntityName}).Devices > 0;
// df.Target = "_frame";
//}
//{
// var df = ListFields.GetField("Kind") as ListField;
// df.GetValue = e => ((Int32)(e as {EntityName}).Kind).ToString("X4");
//}
//ListFields.TraceUrl("TraceId");
}
//private readonly ITracer _tracer;
//public {ClassName}(ITracer tracer)
//{
// _tracer = tracer;
//}
/// <summary>高级搜索。列表页查询、导出Excel、导出Json、分享页等使用</summary>
/// <param name="p">分页器。包含分页排序参数以及Http请求参数</param>
/// <returns></returns>
protected override IEnumerable<{EntityName}> Search(Pager p)
{
//var deviceId = p["deviceId"].ToInt(-1);
//var enable = p["enable"]?.ToBoolean();
var start = p["dtStart"].ToDateTime();
var end = p["dtEnd"].ToDateTime();
return {EntityName}.Search(start, end, p["Q"], p);
}
}
""";
#endregion
#region
/// <summary>生成魔方区域</summary>
/// <param name="option">可选项</param>
/// <returns></returns>
public static Int32 BuildArea(CubeBuilderOption option, ILog log = null)
{
if (option == null)
option = new CubeBuilderOption();
else
option = option.Clone() as CubeBuilderOption;
// 自动识别并修正区域名(主要是大小写)
var areaName = FindAreaName(option.Output.GetBasePath());
if (!areaName.IsNullOrEmpty()) return 0;
// 优先使用路径最后一段作为区域名,其次再用连接名
areaName = Path.GetFileNameWithoutExtension(option.Output);
if (areaName.IsNullOrEmpty())
areaName = option.ConnName;
var file = $"{areaName}Area.cs";
file = option.Output.CombinePath(file);
file = file.GetBasePath();
// 文件已存在,不要覆盖
if (File.Exists(file)) return 0;
// 根命名空间
var root = FindProjectRootNamespace(option.Output);
if (root.IsNullOrEmpty()) root = option.ConnName + "Web";
log?.Info("生成魔方区域 {0} {1}", areaName, file);
var builder = new CubeBuilder
{
RootNamespace = root
};
var code = builder.AreaTemplate;
//code = code.Replace("{Namespace}", option.Namespace);
code = code.Replace("{RootNamespace}", builder.RootNamespace);
code = code.Replace("{Name}", areaName);
code = code.Replace("{DisplayName}", option.DisplayName);
// 输出到文件
file.EnsureDirectory(true);
File.WriteAllText(file, code, Encoding.UTF8);
return 1;
}
/// <summary>生成控制器</summary>
/// <param name="tables">表集合</param>
/// <param name="option">可选项</param>
/// <returns></returns>
public static Int32 BuildControllers(IList<IDataTable> tables, CubeBuilderOption option = null, ILog log = null)
{
if (option == null)
option = new CubeBuilderOption();
else
option = option.Clone() as CubeBuilderOption;
// 根命名空间
var root = FindProjectRootNamespace(option.Output);
if (root.IsNullOrEmpty()) root = option.ConnName + "Web";
// 自动识别并修正区域名(主要是大小写)
var areaName = FindAreaName(option.Output);
if (areaName.IsNullOrEmpty()) areaName = option.ConnName;
if (option.ClassNameTemplate.IsNullOrEmpty()) option.ClassNameTemplate = "{name}Controller";
option.Output = option.Output.CombinePath("Controllers");
log?.Info("生成控制器 {0}", option.Output.GetBasePath());
var count = 0;
var n = tables.Count;
foreach (var item in tables)
{
// 跳过排除项
if (option.Excludes.Contains(item.Name)) continue;
if (option.Excludes.Contains(item.TableName)) continue;
var builder = new CubeBuilder
{
Table = item,
Option = option.Clone(),
AreaName = areaName,
RootNamespace = root,
Sort = n * 10,
Log = log
};
if (builder.Option.BaseClass.IsNullOrEmpty())
{
if (item.InsertOnly)
builder.Option.BaseClass = "ReadOnlyEntityController";
else
builder.Option.BaseClass = "EntityController";
}
builder.Load(item);
builder.Execute();
builder.Save(null, false, false);
count++;
n--;
}
return count;
}
static String FindAreaName(String dir)
{
var di = dir.GetBasePath().AsDirectory();
if (!di.Exists) return null;
foreach (var fi in di.GetFiles("*Area.cs"))
{
var txt = File.ReadAllText(fi.FullName);
var str = txt.Substring("public class", "AreaBase")?.Trim(' ', ':');
if (!str.IsNullOrEmpty())
{
return str.TrimEnd("Area");
}
}
return null;
}
/// <summary>在指定目录中查找项目名</summary>
/// <param name="dir"></param>
/// <returns></returns>
static String FindProjectRootNamespace(String dir)
{
var di = dir.GetBasePath().AsDirectory();
for (var i = 0; i < 3; i++)
{
if (di.Exists)
{
foreach (var fi in di.GetFiles("*.csproj", SearchOption.TopDirectoryOnly))
{
var ns = Path.GetFileNameWithoutExtension(fi.FullName);
var xml = File.ReadAllText(fi.FullName);
if (!xml.IsNullOrEmpty())
{
var str = xml.Substring("<RootNamespace>", "</RootNamespace>");
if (!str.IsNullOrEmpty()) ns = str;
}
if (!ns.IsNullOrEmpty()) return ns;
}
}
di = di.Parent;
}
return null;
}
#endregion
#region
/// <summary>加载数据表</summary>
/// <param name="table"></param>
public override void Load(IDataTable table)
{
Table = table;
var option = Option;
// 命名空间
var str = table.Properties["Namespace"];
if (!str.IsNullOrEmpty()) option.Namespace = str;
// 输出目录
str = table.Properties["CubeOutput"];
if (!str.IsNullOrEmpty()) option.Output = str.GetBasePath();
}
/// <summary>生成前</summary>
protected override void OnExecuting()
{
var opt = Option;
var code = ControllerTemplate;
code = code.Replace("{EntityNamespace}", opt.Namespace);
code = code.Replace("{ClassName}", ClassName);
code = code.Replace("{EntityName}", Table.Name);
code = code.Replace("{RootNamespace}", RootNamespace);
code = code.Replace("{Name}", AreaName);
code = code.Replace("{DisplayName}", Table.Description);
code = code.Replace("{Sort}", Sort + "");
code = code.Replace("{BaseClass}", GetBaseClass());
if (Table.Columns.Any(c => c.Name.EqualIgnoreCase("TraceId")))
code = code.Replace("//ListFields.TraceUrl(", "ListFields.TraceUrl(");
var ss = BuildSearch();
if (!ss.IsNullOrEmpty())
{
var p1 = code.IndexOf(" //var deviceId = p[\"deviceId\"].ToInt(-1);");
var p2 = code.IndexOf("p);", p1);
code = code.Substring(0, p1) + ss + code.Substring(p2 + "p);".Length);
}
Writer.Write(code);
}
/// <summary>生成后</summary>
protected override void OnExecuted() { }
/// <summary>生成主体</summary>
protected override void BuildItems() { }
#endregion
#region
private String BuildSearch()
{
// 收集索引信息,索引中的所有字段都参与,构造一个高级查询模板
var builder = new SearchBuilder(Table) { Nullable = Option.Nullable };
var cs = builder.GetColumns();
if (cs.Count <= 0) return null;
var ps = builder.GetParameters(cs, true, true, true);
var sb = Pool.StringBuilder.Get();
var pis = new List<String>();
foreach (var dc in cs)
{
var name = dc.CamelName();
if (dc.DataType.IsInt())
{
if (dc.DataType.IsEnum)
sb.AppendLine($" var {name} = ({dc.DataType.Name})p[\"{name}\"].ToInt();");
else if (dc.DataType == typeof(Int64))
sb.AppendLine($" var {name} = p[\"{name}\"].ToLong(-1);");
else
sb.AppendLine($" var {name} = p[\"{name}\"].ToInt(-1);");
}
else if (dc.DataType == typeof(Boolean))
sb.AppendLine($" var {name} = p[\"{name}\"]?.ToBoolean();");
else if (dc.DataType == typeof(DateTime))
sb.AppendLine($" var {name} = p[\"{name}\"].ToDateTime();");
else if (dc.DataType == typeof(String))
sb.AppendLine($" var {name} = p[\"{name}\"];");
else
// 不支持的类型,跳过
continue;
pis.Add(name);
}
if (builder.DataTime != null)
{
sb.AppendLine();
sb.AppendLine(" var start = p[\"dtStart\"].ToDateTime();");
sb.AppendLine(" var end = p[\"dtEnd\"].ToDateTime();");
}
sb.AppendLine();
sb.AppendLine($" return {Table.Name}.Search({pis.Join(", ")}, p[\"Q\"], p);");
return sb.Return(true).TrimEnd();
}
#endregion
}