509 lines
20 KiB
C#
509 lines
20 KiB
C#
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Drawing.Drawing2D;
|
|
using NPOI.OpenXmlFormats.Dml.Diagram;
|
|
using SqlSugar;
|
|
using Tnb.Core;
|
|
using Tnb.Vengine.DataAccess;
|
|
|
|
namespace Tnb.Vengine.Domain;
|
|
|
|
internal class VmQueryParser
|
|
{
|
|
public const string MAIN_ALIES = "m";
|
|
public const char NAVI_SEPERATE = '.';
|
|
public const char PROP_SEPERATE = '&';
|
|
|
|
private readonly IDataAccess _dataAccess;
|
|
private readonly Vmodel _root;
|
|
private readonly VmQueryInput _input;
|
|
|
|
public Dictionary<string, VmNavigate> Navigates { get; } = new Dictionary<string, VmNavigate>();
|
|
public VmQueryParser(IDataAccess dataAccess, Vmodel rootModel, VmQueryInput input)
|
|
{
|
|
_dataAccess = dataAccess;
|
|
_root = rootModel;
|
|
_input = input;
|
|
}
|
|
/// <summary>
|
|
/// 解析查询参数
|
|
/// </summary>
|
|
public void ParseQueryInput()
|
|
{
|
|
// 初始化根模型
|
|
Navigates.Clear();
|
|
VmNavigate main = new(MAIN_ALIES);
|
|
main.navConfig.naviModel = _root;
|
|
Navigates.Add(MAIN_ALIES, main);
|
|
|
|
ParseOutputStr(_input.o);
|
|
ParseQueryPara(_input.q);
|
|
ParseSortPara(_input.sort);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除字符串特殊符号
|
|
/// </summary>
|
|
private string ClearStr(string str)
|
|
{
|
|
return str.Trim(' ', '\r', '\n', '\t');
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析输出属性字符串
|
|
/// </summary>
|
|
private void ParseOutputStr(string output)
|
|
{
|
|
// t1.t2.id
|
|
List<string> outputs = output.Split(',').Distinct().ToList();
|
|
foreach (string? s in outputs)
|
|
{
|
|
var outStr = ClearStr(s);
|
|
if (string.IsNullOrWhiteSpace(outStr))
|
|
{
|
|
continue;
|
|
}
|
|
// t1 t2 id
|
|
string[] codes = outStr.Split(NAVI_SEPERATE);
|
|
|
|
// 根据导航路径添加选择器
|
|
string vmPath = MAIN_ALIES;
|
|
for (int i = 0; i < codes.Length - 1; i++)
|
|
{
|
|
string code = ClearStr(codes[i]);
|
|
vmPath = i == 0 ? code : vmPath + NAVI_SEPERATE + code;
|
|
if (!Navigates.ContainsKey(vmPath))
|
|
{
|
|
Navigates.Add(vmPath, new VmNavigate(vmPath));
|
|
}
|
|
}
|
|
|
|
// 添加返回的属性到选择器
|
|
string lastCode = ClearStr(codes[^1]);
|
|
Navigates[vmPath].selects.Add(new VmSelectProp(lastCode));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析查询参数
|
|
/// </summary>
|
|
private void ParseQueryPara(DObject? query)
|
|
{
|
|
if (query == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (KeyValuePair<string, object> item in query)
|
|
{
|
|
string[] codes = item.Key.Split(NAVI_SEPERATE);
|
|
|
|
// 将导航属性查询条件添加到选择器
|
|
string vmPath = MAIN_ALIES;
|
|
for (int i = 0; i < codes.Length - 1; i++)
|
|
{
|
|
string code = ClearStr(codes[i]);
|
|
vmPath = i == 0 ? code : vmPath + NAVI_SEPERATE + code;
|
|
if (!Navigates.ContainsKey(vmPath))
|
|
{
|
|
Navigates.Add(vmPath, new VmNavigate(vmPath));
|
|
}
|
|
}
|
|
// 添加返回的属性到选择器
|
|
string lastCode = ClearStr(codes[^1]);
|
|
Navigates[vmPath].wheres.Add(new VmWhereProp(lastCode) { value = item.Value });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析排序参数
|
|
/// </summary>
|
|
private void ParseSortPara(string? sort)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(sort))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string[] orders = sort.Split(',');
|
|
foreach (string s in orders)
|
|
{
|
|
var orderStr = ClearStr(s);
|
|
if (string.IsNullOrWhiteSpace(orderStr))
|
|
{
|
|
continue;
|
|
}
|
|
// 拆分 m.code desc
|
|
string[] codes = orderStr.Split(' ', 2);
|
|
// 拆分 m.code
|
|
(string?, string) orderPath = codes[0].GetParent(NAVI_SEPERATE);
|
|
orderPath.Item1 ??= MAIN_ALIES;
|
|
ThrowIf.When(!Navigates.ContainsKey(orderPath.Item1), $"排序参数{orderStr}错误,导航路径{orderPath.Item1}必须包含在输出参数中");
|
|
|
|
VmOrderProp orderby = new(orderPath.Item2);
|
|
if (codes.Length == 2)
|
|
{
|
|
OrderByType? orderType = codes[1].ToLower() switch
|
|
{
|
|
"asc" => OrderByType.Asc,
|
|
"desc" => OrderByType.Desc,
|
|
_ => null
|
|
};
|
|
ThrowIf.IsNull(orderType, $"排序方式{orderStr}不正确");
|
|
orderby.orderType = orderType.Value;
|
|
}
|
|
Navigates[orderPath.Item1].orders.Add(orderby);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载单个导航属性模型
|
|
/// </summary>
|
|
private async Task<Vmodel> LoadVmNavigateModelAsync(VmNavigate nav, Dictionary<string, VmNavigate> navs)
|
|
{
|
|
if (nav.navConfig.naviModel != null)
|
|
{
|
|
return nav.navConfig.naviModel;
|
|
}
|
|
|
|
// 分割导航路径, a.b.c -> a.b, c
|
|
(string?, string) path = nav.path.GetParent(NAVI_SEPERATE);
|
|
path.Item1 ??= MAIN_ALIES;
|
|
nav.parent = Navigates[path.Item1];
|
|
Vmodel parentVm = await LoadVmNavigateModelAsync(nav.parent, navs);
|
|
|
|
// 获取导航属性配置
|
|
nav.navConfig = parentVm.navProps.FirstOrDefault(a => a.code == path.Item2)!;
|
|
ThrowIf.IsNull(nav.navConfig, $"模型({parentVm.fullCode})的导航属性{nav.path}配置错误: 找不到(code = {path.Item2})的导航属性");
|
|
|
|
// 获取导航模型
|
|
nav.navConfig.naviModel = await _dataAccess.GetVmodelAsync(nav.navConfig.vmid);
|
|
ThrowIf.IsNull(nav.navConfig.naviModel, $"模型({parentVm.fullCode})的导航属性{nav.path}配置错误: 找不到(id = {nav.navConfig.vmid})的模型");
|
|
|
|
// 处理导航模型
|
|
if (nav.navConfig.refCode == MAIN_ALIES)
|
|
{
|
|
var field = nav.parent.navConfig.naviModel!.PropToFieldCode(nav.navConfig.refProp);
|
|
ThrowIf.IsNull(field, $"模型({parentVm.fullCode})的导航属性{nav.path}配置错误: 找不到(code = {nav.navConfig.refProp})的字段");
|
|
field = nav.navConfig.naviModel.PropToFieldCode(nav.navConfig.fkProp);
|
|
ThrowIf.IsNull(field, $"模型({parentVm.fullCode})的导航属性{nav.path}配置错误: 在子模型{nav.navConfig.naviModel.fullCode}中找不到(code = {nav.navConfig.fkProp})的字段");
|
|
if (!nav.parent.selects.Any(a => a.code == nav.navConfig.refProp))
|
|
{
|
|
nav.parent.selects.Add(new VmSelectProp(nav.navConfig.refProp));
|
|
}
|
|
if (!nav.selects.Any(a => a.code == nav.navConfig.fkProp))
|
|
{
|
|
nav.selects.Add(new VmSelectProp(nav.navConfig.fkProp));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var navPath = nav.path + NAVI_SEPERATE + nav.navConfig.refCode;
|
|
if (!Navigates.ContainsKey(navPath))
|
|
{
|
|
VmNavigate midNav = new VmNavigate(navPath);
|
|
midNav.navConfig = parentVm.navProps.FirstOrDefault(a => a.code == nav.navConfig.refCode)!;
|
|
ThrowIf.IsNull(midNav.navConfig, $"模型({parentVm.fullCode})的导航属性{midNav.path}配置错误: 找不到(code = {nav.navConfig.refCode})的导航属性");
|
|
midNav.navConfig.naviModel = await _dataAccess.GetVmodelAsync(midNav.navConfig.vmid);
|
|
ThrowIf.IsNull(midNav.navConfig.naviModel, $"模型({parentVm.fullCode})的导航属性{midNav.path}配置错误: 找不到(id = {midNav.navConfig.vmid})的模型");
|
|
}
|
|
}
|
|
|
|
return nav.navConfig.naviModel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载单个导航属性模型
|
|
/// </summary>
|
|
public async Task LoadNavigateAsync()
|
|
{
|
|
Dictionary<string, VmNavigate> tobeAdd = new Dictionary<string, VmNavigate>();
|
|
foreach (VmNavigate nav in Navigates.Values)
|
|
{
|
|
_ = await LoadVmNavigateModelAsync(nav, tobeAdd);
|
|
}
|
|
foreach (var item in tobeAdd)
|
|
{
|
|
Navigates.Add(item.Key, item.Value);
|
|
}
|
|
foreach (VmNavigate nav in Navigates.Values)
|
|
{
|
|
nav.HandleStar();
|
|
nav.LoadDbProp();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 处理一对一联表关系,生成联表信息
|
|
/// </summary>
|
|
/// <param name="selProps"></param>
|
|
/// <remarks>SELECT * FROM wms_distask_h m INNER JOIN eqp_equipment equip ON m.device_id=equip."id" INNER JOIN eqp_equip_type equipType ON equip.equip_type_id=equipType."id"</remarks>
|
|
/// <returns></returns>
|
|
public List<JoinInfoParameter> GetJoinInfos()
|
|
{
|
|
List<JoinInfoParameter> joins = new();
|
|
// 主查询中只处理主表和一对一的联表
|
|
IEnumerable<VmNavigate> navs = Navigates.Values.Where(a => a.path != MAIN_ALIES && a.navConfig.navType == eNavigateType.OneToOne);
|
|
foreach (VmNavigate? nav in navs)
|
|
{
|
|
Debug.Assert(nav.parent != null && nav.navConfig.naviModel != null);
|
|
JoinInfoParameter join = new() { TableName = nav.navConfig.naviModel.tableName, ShortName = nav.pathCode, Type = JoinType.Left };
|
|
var refNav = nav.navConfig.refCode == MAIN_ALIES ? nav.parent : Navigates[nav.path + "." + nav.navConfig.refCode];
|
|
var fkField = nav.navConfig.naviModel.PropToFieldCode(nav.navConfig.fkProp);
|
|
var refField = refNav.navConfig.naviModel!.PropToFieldCode(nav.navConfig.refProp);
|
|
join.Models = ObjectFuncModel.Create("Equals", $"{nav.pathCode}.{fkField}", $"{refNav.pathCode}.{refField}");
|
|
joins.Add(join);
|
|
}
|
|
return joins;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成查询过滤条件
|
|
/// </summary>
|
|
/// <param name="filter"></param>
|
|
/// <returns></returns>
|
|
public List<IConditionalModel> GetConditionalModels()
|
|
{
|
|
List<IConditionalModel> wheres = new();
|
|
foreach (VmNavigate? nav in Navigates.Values.Where(a => (a.path == MAIN_ALIES || a.navConfig.navType == eNavigateType.OneToOne) && a.wheres.Count > 0))
|
|
{
|
|
foreach (VmWhereProp where in nav.wheres)
|
|
{
|
|
if (where.value is IEnumerable<object> arrObj)
|
|
{
|
|
object[] val = arrObj.ToArray();
|
|
string op = val[0].ToString()!;
|
|
switch (op)
|
|
{
|
|
case "><":
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[1].ToString(), ConditionalType = ConditionalType.GreaterThan, CSharpTypeName = where.csType });
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[2].ToString(), ConditionalType = ConditionalType.LessThan, CSharpTypeName = where.csType });
|
|
break;
|
|
case ">=<":
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[1].ToString(), ConditionalType = ConditionalType.GreaterThanOrEqual, CSharpTypeName = where.csType });
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[2].ToString(), ConditionalType = ConditionalType.LessThan, CSharpTypeName = where.csType });
|
|
break;
|
|
case "><=":
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[1].ToString(), ConditionalType = ConditionalType.GreaterThan, CSharpTypeName = where.csType });
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[2].ToString(), ConditionalType = ConditionalType.LessThanOrEqual, CSharpTypeName = where.csType });
|
|
break;
|
|
case ">=<=":
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[1].ToString(), ConditionalType = ConditionalType.GreaterThanOrEqual, CSharpTypeName = where.csType });
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = val[2].ToString(), ConditionalType = ConditionalType.LessThanOrEqual, CSharpTypeName = where.csType });
|
|
break;
|
|
case "in":
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = string.Join(',', val.Skip(1)), ConditionalType = ConditionalType.In, CSharpTypeName = where.csType });
|
|
break;
|
|
default: op = string.Empty; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ConditionalType conditionalType = ConditionalType.Equal;
|
|
string? value = where.value?.ToString();
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (value.Length >= 2)
|
|
{
|
|
string op = value[..2];
|
|
switch (op)
|
|
{
|
|
case "%%": conditionalType = ConditionalType.Like; break;
|
|
case ">>": conditionalType = ConditionalType.GreaterThan; break;
|
|
case "<<": conditionalType = ConditionalType.LessThan; break;
|
|
case ">=": conditionalType = ConditionalType.GreaterThanOrEqual; break;
|
|
case "<=": conditionalType = ConditionalType.LessThanOrEqual; break;
|
|
case "==": conditionalType = ConditionalType.Equal; break;
|
|
default: op = string.Empty; break;
|
|
}
|
|
if (!string.IsNullOrEmpty(op))
|
|
{
|
|
value = value.RemovePreFix(op);
|
|
if (value.ToLower() == "null")
|
|
{
|
|
value = null;
|
|
}
|
|
}
|
|
}
|
|
wheres.Add(new ConditionalModel { FieldName = where.fieldName, FieldValue = value, ConditionalType = conditionalType, CSharpTypeName = where.csType });
|
|
}
|
|
}
|
|
}
|
|
return wheres;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成按关键字查询的条件列表
|
|
/// </summary>
|
|
/// <param name="drill">是否钻取子表关键词查询</param>
|
|
/// <returns></returns>
|
|
public ConditionalCollections GetKeywordConditional(bool drill = true)
|
|
{
|
|
ConditionalCollections conditionals = new() { ConditionalList = new List<KeyValuePair<WhereType, ConditionalModel>>() };
|
|
if (string.IsNullOrWhiteSpace(_input.k))
|
|
{
|
|
return conditionals;
|
|
}
|
|
|
|
WhereType wType = WhereType.And;
|
|
IEnumerable<VmNavigate> navs = Navigates.Values.WhereIF(!drill, a => a.path == MAIN_ALIES).Where(a => a.navConfig.navType != eNavigateType.OneToMany);
|
|
foreach (VmNavigate? nav in navs)
|
|
{
|
|
foreach (VmSelectProp? prop in nav.selects.Where(a => a.fuzzy))
|
|
{
|
|
conditionals.ConditionalList.Add(new(wType, new ConditionalModel() { FieldName = prop.fieldName, ConditionalType = ConditionalType.Like, FieldValue = _input.k, CSharpTypeName = prop.csType }));
|
|
wType = WhereType.Or;
|
|
}
|
|
}
|
|
return conditionals;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成排序条件列表
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<OrderByModel> GetOrderByModels()
|
|
{
|
|
IEnumerable<VmNavigate> navs = Navigates.Values.Where(a => a.orders.Count > 0);
|
|
return navs.SelectMany(a =>
|
|
{
|
|
return a.orders.Select(b => new OrderByModel
|
|
{
|
|
FieldName = b.fieldName,
|
|
OrderByType = b.orderType
|
|
});
|
|
}).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成查询字段列表
|
|
/// </summary>
|
|
/// <param name="selProps"></param>
|
|
/// <returns></returns>
|
|
public List<SelectModel> GetSelectModels()
|
|
{
|
|
IEnumerable<VmNavigate> navs = Navigates.Values.Where(a => (a.path == MAIN_ALIES || a.navConfig.navType == eNavigateType.OneToOne) && a.selects.Count > 0);
|
|
return navs.SelectMany(a =>
|
|
{
|
|
return a.selects.Select(b => new SelectModel
|
|
{
|
|
FiledName = b.fieldName,
|
|
AsName = (a.path == MAIN_ALIES ? "" : a.pathCode + PROP_SEPERATE) + b.code
|
|
});
|
|
}).ToList();
|
|
}
|
|
|
|
}
|
|
|
|
internal class VmNavigate
|
|
{
|
|
public VmNavigate? parent { get; set; }
|
|
public VmNavProp navConfig { get; set; } = new VmNavProp();
|
|
public string path { get; set; }
|
|
public string pathCode { get; set; }
|
|
|
|
public List<VmSelectProp> selects { get; set; } = new List<VmSelectProp>();
|
|
public List<VmWhereProp> wheres { get; set; } = new List<VmWhereProp>();
|
|
public List<VmOrderProp> orders { get; set; } = new List<VmOrderProp>();
|
|
|
|
public VmNavigate(string vmPath)
|
|
{
|
|
path = vmPath;
|
|
pathCode = vmPath.Replace(VmQueryParser.NAVI_SEPERATE, VmQueryParser.PROP_SEPERATE);
|
|
}
|
|
/// <summary>
|
|
/// 处理占位符星号
|
|
/// </summary>
|
|
public void HandleStar()
|
|
{
|
|
if (selects.Any(a => a.code == "*"))
|
|
{
|
|
selects.Clear();
|
|
navConfig.naviModel!.dbProps.ForEach(a =>
|
|
{
|
|
selects.Add(new VmSelectProp(a.code));
|
|
});
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 将属性名转换为字段名
|
|
/// </summary>
|
|
public void LoadDbProp()
|
|
{
|
|
foreach (VmSelectProp item in selects)
|
|
{
|
|
VmDbProp? prop = navConfig.naviModel!.GetDbProp(item.code);
|
|
ThrowIf.IsNull(prop, $"输出参数{path}.{item.code}错误: 在模型{navConfig.naviModel.fullCode}中找不到属性({item.code})对应的字段");
|
|
item.field = prop.field;
|
|
item.fieldName = pathCode + "." + item.field;
|
|
item.fuzzy = prop.fuzzy;
|
|
item.csType = prop.csType;
|
|
item.asName = (path == VmQueryParser.MAIN_ALIES ? "" : pathCode + VmQueryParser.PROP_SEPERATE) + item.code;
|
|
}
|
|
foreach (VmOrderProp item in orders)
|
|
{
|
|
VmDbProp? prop = navConfig.naviModel!.GetDbProp(item.code);
|
|
ThrowIf.IsNull(prop, $"排序参数{path}.{item.code}错误: 在模型{navConfig.naviModel.fullCode}中找不到属性({item.code})对应的字段");
|
|
item.field = prop.field;
|
|
item.fieldName = pathCode + "." + item.field;
|
|
}
|
|
foreach (VmWhereProp item in wheres)
|
|
{
|
|
VmDbProp? prop = navConfig.naviModel!.GetDbProp(item.code);
|
|
ThrowIf.IsNull(prop, $"查询参数{path}.{item.code}错误: 在模型{navConfig.naviModel.fullCode}中找不到属性({item.code})对应的字段");
|
|
item.field = prop.field;
|
|
item.fieldName = pathCode + "." + item.field;
|
|
item.csType = prop.csType;
|
|
}
|
|
//props.RemoveAll(a => string.IsNullOrEmpty(a.field));
|
|
}
|
|
}
|
|
|
|
internal class VmQueryBaseProp
|
|
{
|
|
public string code { get; set; }
|
|
public string field { get; set; } = string.Empty;
|
|
public string fieldName { get; set; } = string.Empty;
|
|
|
|
public VmQueryBaseProp(string code)
|
|
{
|
|
this.code = code;
|
|
}
|
|
|
|
}
|
|
|
|
internal class VmSelectProp : VmQueryBaseProp
|
|
{
|
|
public string asName { get; set; } = string.Empty;
|
|
public string csType { get; set; } = "string";
|
|
public bool fuzzy { get; set; }
|
|
|
|
public VmSelectProp(string code) : base(code)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal class VmOrderProp : VmQueryBaseProp
|
|
{
|
|
public OrderByType orderType { get; set; } = OrderByType.Asc;
|
|
|
|
public VmOrderProp(string code) : base(code)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal class VmWhereProp : VmQueryBaseProp
|
|
{
|
|
public string csType { get; set; } = "string";
|
|
[AllowNull]
|
|
public object value { get; set; }
|
|
|
|
public VmWhereProp(string code) : base(code)
|
|
{
|
|
}
|
|
}
|
|
|