模型查询接口增加按子表字段查询
This commit is contained in:
@@ -71,7 +71,8 @@
|
|||||||
"txt",
|
"txt",
|
||||||
"rar",
|
"rar",
|
||||||
"zip",
|
"zip",
|
||||||
"csv"
|
"csv",
|
||||||
|
"json"
|
||||||
],
|
],
|
||||||
//过滤上传文件名称特殊字符
|
//过滤上传文件名称特殊字符
|
||||||
"SpecialString": [
|
"SpecialString": [
|
||||||
@@ -188,5 +189,5 @@
|
|||||||
"DoMainApp": "http://localhost:8081", // 前端App外网能访问的地址(域名), 回调的时候拼接接口地址用
|
"DoMainApp": "http://localhost:8081", // 前端App外网能访问的地址(域名), 回调的时候拼接接口地址用
|
||||||
"AppPushUrl": "https://8e84eea8-6922-4033-8e86-67ad7442e692.bspapp.com/unipush"
|
"AppPushUrl": "https://8e84eea8-6922-4033-8e86-67ad7442e692.bspapp.com/unipush"
|
||||||
},
|
},
|
||||||
"IsStartTimeJob": true //是否开启定时任务
|
"IsStartTimeJob": false //是否开启定时任务
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using JNPF.Common.Extension;
|
using JNPF.Common.Extension;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
namespace System;
|
namespace System;
|
||||||
|
|
||||||
@@ -183,4 +184,17 @@ public static class StringExtensions
|
|||||||
public static string ToCamel(this string str) => str.SplitWord().Select((a, i) => i == 0 ? a : a.UpperFirst()).JoinAsString("");
|
public static string ToCamel(this string str) => str.SplitWord().Select((a, i) => i == 0 ? a : a.UpperFirst()).JoinAsString("");
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public static (string, string) LastSplit(this string str, char seperate)
|
||||||
|
{
|
||||||
|
int n = str.LastIndexOf(seperate);
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
return (str.Substring(0, n), str.Substring(n + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (str, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Cryptography.Xml;
|
||||||
using JNPF;
|
using JNPF;
|
||||||
using JNPF.Common.Core.Manager;
|
using JNPF.Common.Core.Manager;
|
||||||
|
using JNPF.Common.Extension;
|
||||||
using JNPF.DependencyInjection;
|
using JNPF.DependencyInjection;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
@@ -15,7 +17,7 @@ namespace Tnb.Vengine.DataAccess;
|
|||||||
public class DataAccess : IDataAccess, ITransient, IDisposable
|
public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||||
{
|
{
|
||||||
private const int MAX_PAGE_SIZE = 1000;
|
private const int MAX_PAGE_SIZE = 1000;
|
||||||
private ISqlSugarClient? _db;
|
private static ISqlSugarClient? _db;
|
||||||
|
|
||||||
protected ISqlSugarClient Db
|
protected ISqlSugarClient Db
|
||||||
{
|
{
|
||||||
@@ -170,14 +172,18 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
private async Task LoadVmodelNavigateAsync(Vmodel vm)
|
private async Task LoadVmodelNavigateAsync(Vmodel vm)
|
||||||
{
|
{
|
||||||
Dictionary<string, Vmodel> dictVm = new();
|
Dictionary<string, Vmodel> dictVm = new();
|
||||||
|
var vmids = vm.navProps.Select(a => a.vmid).Distinct().ToList();
|
||||||
|
var ls = await Db.Queryable<Vmodel>().Where(a => vmids.Contains(a.id)).ToListAsync();
|
||||||
|
var navs = ls.ToDictionary(a => a.id);
|
||||||
foreach (var navProp in vm.navProps)
|
foreach (var navProp in vm.navProps)
|
||||||
{
|
{
|
||||||
if (!dictVm.ContainsKey(navProp.vmid))
|
navProp.naviModel = (Vmodel)navs.GetOrDefault(navProp.vmid);
|
||||||
{
|
//if (!dictVm.ContainsKey(navProp.vmid))
|
||||||
var navModel = await GetVmodelAsync(navProp.vmid);
|
//{
|
||||||
dictVm.Add(navProp.vmid, navModel);
|
// var navModel = await GetVmodelAsync(navProp.vmid);
|
||||||
}
|
// dictVm.Add(navProp.vmid, navModel);
|
||||||
navProp.naviModel = dictVm[navProp.vmid];
|
//}
|
||||||
|
//navProp.naviModel = dictVm[navProp.vmid];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,10 +197,8 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
var selProps = vm.GetVmSelectProps(input.o);
|
var selProps = vm.GetVmSelectProps(input.o);
|
||||||
//处理导航属性联表
|
//处理导航属性联表
|
||||||
List<JoinInfoParameter> joins = vm.GetJoinInfos(selProps);
|
List<JoinInfoParameter> joins = vm.GetJoinInfos(selProps);
|
||||||
query.AddJoinInfo(joins);
|
|
||||||
//if (joins.Count > 0)
|
//if (joins.Count > 0)
|
||||||
//{
|
query.AddJoinInfo(joins);
|
||||||
//}
|
|
||||||
List<IConditionalModel> wheres = vm.GetConditionalModels(input.q);
|
List<IConditionalModel> wheres = vm.GetConditionalModels(input.q);
|
||||||
if (!string.IsNullOrEmpty(input.k))
|
if (!string.IsNullOrEmpty(input.k))
|
||||||
{
|
{
|
||||||
@@ -208,10 +212,8 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
wheres.Add(new ConditionalCollections() { ConditionalList = lsCondition });
|
wheres.Add(new ConditionalCollections() { ConditionalList = lsCondition });
|
||||||
}
|
}
|
||||||
//处理查询参数
|
//处理查询参数
|
||||||
query.Where(wheres);
|
|
||||||
//if (wheres.Count > 0)
|
//if (wheres.Count > 0)
|
||||||
//{
|
query.Where(wheres);
|
||||||
//}
|
|
||||||
if (!string.IsNullOrEmpty(input.sort))
|
if (!string.IsNullOrEmpty(input.sort))
|
||||||
{
|
{
|
||||||
query.OrderBy(input.sort);
|
query.OrderBy(input.sort);
|
||||||
@@ -229,7 +231,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
//组装输出对象
|
//组装输出对象
|
||||||
foreach (var data in ls)
|
foreach (var data in ls)
|
||||||
{
|
{
|
||||||
DObject ret = await NestedOutputAsync(vm, new DObject(data), selProps);
|
DObject ret = await CombineOutputAsync(vm, new DObject(data), selProps);
|
||||||
result.items.Add(ret);
|
result.items.Add(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,11 +245,12 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
/// <param name="src"></param>
|
/// <param name="src"></param>
|
||||||
/// <param name="selProps"></param>
|
/// <param name="selProps"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<DObject> NestedOutputAsync(Vmodel vm, DObject src, List<VmSelectProp> selProps)
|
private async Task<DObject> CombineOutputAsync(Vmodel vm, DObject src, List<VmSelectProp> selProps)
|
||||||
{
|
{
|
||||||
DObject ret = new();
|
DObject ret = new();
|
||||||
foreach (var prop in selProps)
|
foreach (var prop in selProps)
|
||||||
{
|
{
|
||||||
|
// 加载主表字段
|
||||||
if (prop.navType == eNavigateType.None || prop.navCode == VmSelectProp.MAIN_ALIES)
|
if (prop.navType == eNavigateType.None || prop.navCode == VmSelectProp.MAIN_ALIES)
|
||||||
{
|
{
|
||||||
if (src.ContainsKey(prop.code))
|
if (src.ContainsKey(prop.code))
|
||||||
@@ -257,40 +260,14 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// 加载关联表字段
|
||||||
if (prop.navType == eNavigateType.OneToOne)
|
if (prop.navType == eNavigateType.OneToOne)
|
||||||
{
|
{
|
||||||
//以 nav_prop的形式返回
|
NestedOutput(vm, src, ret, prop);
|
||||||
var key = prop.navCode + "_" + prop.code;
|
|
||||||
ret.Add(key, src[key]);
|
|
||||||
//以 nav.prop的形式返回
|
|
||||||
//if (!ret.ContainsKey(prop.navCode))
|
|
||||||
//{
|
|
||||||
// ret.Add(prop.navCode, new DObject());
|
|
||||||
//}
|
|
||||||
//var key = prop.navCode + "_" + prop.code;
|
|
||||||
//if (src.ContainsKey(key))
|
|
||||||
//{
|
|
||||||
// ((DObject)ret[prop.navCode]).Add(prop.code, src[key]);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
else if (prop.navType == eNavigateType.OneToMany)
|
else if (prop.navType == eNavigateType.OneToMany)
|
||||||
{
|
{
|
||||||
if (!ret.ContainsKey(prop.navCode))
|
await NestedOneToManyAsync(vm, src, ret, prop, selProps);
|
||||||
{
|
|
||||||
ret.Add(prop.navCode, new List<DObject>());
|
|
||||||
}
|
|
||||||
var navProp = vm.navProps.First(a => a.code == prop.navCode);
|
|
||||||
if (navProp != null && navProp.naviModel != null && src.ContainsKey(navProp.refField))
|
|
||||||
{
|
|
||||||
VmListInput input = new VmListInput();
|
|
||||||
var fkProp = navProp.naviModel.FieldCodeToPropCode(navProp.fkField);
|
|
||||||
if (!string.IsNullOrEmpty(fkProp))
|
|
||||||
{
|
|
||||||
input.q = new DObject(fkProp, src[navProp.refField]);
|
|
||||||
input.o = string.Join(',', selProps.Where(a => a.navCode == prop.navCode).Select(a => a.code));
|
|
||||||
ret[prop.navCode] = (await QueryDataAsync(navProp.naviModel, input)).items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (prop.navType == eNavigateType.ManyToMany)
|
else if (prop.navType == eNavigateType.ManyToMany)
|
||||||
{
|
{
|
||||||
@@ -304,6 +281,50 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将一对一的关联表字段嵌入到返回值中
|
||||||
|
/// </summary>
|
||||||
|
private void NestedOutput(Vmodel vm, DObject src, DObject ret, VmSelectProp prop)
|
||||||
|
{
|
||||||
|
// 以 nav_prop的形式返回
|
||||||
|
var key = prop.navCode + "_" + prop.code;
|
||||||
|
ret.Add(key, src[key]);
|
||||||
|
// 以 nav.prop的形式返回
|
||||||
|
//if (!ret.ContainsKey(prop.navCode))
|
||||||
|
//{
|
||||||
|
// ret.Add(prop.navCode, new DObject());
|
||||||
|
//}
|
||||||
|
//var key = prop.navCode + "_" + prop.code;
|
||||||
|
//if (src.ContainsKey(key))
|
||||||
|
//{
|
||||||
|
// ((DObject)ret[prop.navCode]).Add(prop.code, src[key]);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task NestedOneToManyAsync(Vmodel vm, DObject src, DObject ret, VmSelectProp prop, List<VmSelectProp> selProps)
|
||||||
|
{
|
||||||
|
// 在返回值中增加导航属性
|
||||||
|
if (ret.ContainsKey(prop.navCode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret.Add(prop.navCode, new List<DObject>());
|
||||||
|
|
||||||
|
// 找到导航属性的配置
|
||||||
|
var navCfg = vm.navProps.First(a => a.code == prop.navCode);
|
||||||
|
if (navCfg != null && navCfg.naviModel != null && src.ContainsKey(navCfg.refField) && navCfg.refCode == VmSelectProp.MAIN_ALIES)
|
||||||
|
{
|
||||||
|
VmListInput input = new VmListInput();
|
||||||
|
var fkProp = navCfg.naviModel.FieldCodeToPropCode(navCfg.fkField);
|
||||||
|
if (!string.IsNullOrEmpty(fkProp))
|
||||||
|
{
|
||||||
|
input.q = new DObject(fkProp, src[navCfg.refField]);
|
||||||
|
input.o = string.Join(',', selProps.Where(a => a.navCode == prop.navCode).Select(a => a.code));
|
||||||
|
ret[prop.navCode] = (await QueryDataAsync(navCfg.naviModel, input)).items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 新增数据 默认方法
|
/// 新增数据 默认方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
97
visualdev/Tnb.Vengine/Domain/OutputParser.cs
Normal file
97
visualdev/Tnb.Vengine/Domain/OutputParser.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JNPF.Common.Extension;
|
||||||
|
using Tnb.Vengine.DataAccess;
|
||||||
|
|
||||||
|
namespace Tnb.Vengine.Domain
|
||||||
|
{
|
||||||
|
public class OutputParser
|
||||||
|
{
|
||||||
|
private readonly IDataAccess _dataAccess;
|
||||||
|
private readonly Vmodel _root;
|
||||||
|
private List<string> _outputs;
|
||||||
|
private Dictionary<string, OutputSelect> _selectProps = new Dictionary<string, OutputSelect>();
|
||||||
|
private Dictionary<string, Vmodel?> _navModels = new Dictionary<string, Vmodel?>();
|
||||||
|
public OutputParser(IDataAccess dataAccess, Vmodel rootModel, string output)
|
||||||
|
{
|
||||||
|
_dataAccess = dataAccess;
|
||||||
|
_root = rootModel;
|
||||||
|
_outputs = output.Split(',').Distinct().ToList();
|
||||||
|
ParseOutputStr();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 按模型组织要输出的属性
|
||||||
|
/// </summary>
|
||||||
|
private void ParseOutputStr()
|
||||||
|
{
|
||||||
|
foreach (var outStr in _outputs)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(outStr))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string vmPath;
|
||||||
|
int n = outStr.LastIndexOf('.');
|
||||||
|
if (n < 1)
|
||||||
|
{
|
||||||
|
vmPath = Vmodel.MAIN_ALIES;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vmPath = outStr.Substring(0, n);
|
||||||
|
}
|
||||||
|
if (!_selectProps.ContainsKey(vmPath))
|
||||||
|
{
|
||||||
|
_selectProps.Add(vmPath, new OutputSelect { navPaths = vmPath.Split(',').ToList(), vmPath = vmPath });
|
||||||
|
}
|
||||||
|
var outSelect = _selectProps[vmPath];
|
||||||
|
outSelect.propCodes.Add(outStr.Substring(n + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadNavModel()
|
||||||
|
{
|
||||||
|
var navProps = _root.navProps.Where(a => _selectProps.Values.Any(b => b.vmPath.StartsWith(a.code + "."))).ToList();
|
||||||
|
await LoadVmodelNavigateAsync(navProps);
|
||||||
|
}
|
||||||
|
private async Task LoadVmodelNavigateAsync(List<VmNavProp> navProps)
|
||||||
|
{
|
||||||
|
var db = _dataAccess.GetSqlSugar();
|
||||||
|
|
||||||
|
var vmids = navProps.Select(a => a.vmid).Distinct().ToList();
|
||||||
|
var navs = await db.Queryable<Vmodel>().Where(a => vmids.Contains(a.id)).ToDictionaryAsync(a => a.id, a => a);
|
||||||
|
foreach (var navProp in navProps)
|
||||||
|
{
|
||||||
|
navProp.naviModel = (Vmodel)navs.GetOrDefault(navProp.vmid);
|
||||||
|
navProp.naviModel.navProps.Where(a => _selectProps.Values.Any(b => b.vmPath.StartsWith(a.code + "."))).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class OutputSelect
|
||||||
|
{
|
||||||
|
public string vmId { get; set; } = string.Empty;
|
||||||
|
public string vmCode { get; set; } = string.Empty;
|
||||||
|
public string vmPath { get; set; } = string.Empty;
|
||||||
|
public eNavigateType navType { get; set; } = eNavigateType.None;
|
||||||
|
public List<string> navPaths { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public List<string> propCodes { get; set; } = new List<string>();
|
||||||
|
public List<string> fieldCodes { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public OutputSelect()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public OutputSelect(Vmodel model)
|
||||||
|
{
|
||||||
|
vmId = model.id;
|
||||||
|
vmCode = model.vmCode;
|
||||||
|
vmPath = Vmodel.MAIN_ALIES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,6 +164,7 @@ public class VmSelectProp
|
|||||||
public const string MAIN_ALIES = "m";
|
public const string MAIN_ALIES = "m";
|
||||||
public string code { get; set; } = string.Empty;
|
public string code { get; set; } = string.Empty;
|
||||||
public string field { get; set; } = string.Empty;
|
public string field { get; set; } = string.Empty;
|
||||||
|
public List<string> navPath { get; set; } = new List<string>();
|
||||||
public string navCode { get; set; } = MAIN_ALIES;
|
public string navCode { get; set; } = MAIN_ALIES;
|
||||||
public ePropType propType { get; set; }
|
public ePropType propType { get; set; }
|
||||||
public eNavigateType navType { get; set; }
|
public eNavigateType navType { get; set; }
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class VmDbProp : VmBaseProp
|
|||||||
/// 获取默认值文本
|
/// 获取默认值文本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public object? GetDefaultValueString()
|
public string? GetDefaultValueString()
|
||||||
{
|
{
|
||||||
string val = "";
|
string val = "";
|
||||||
if (!required) return val;
|
if (!required) return val;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace Tnb.Vengine.Domain;
|
|||||||
[SugarTable("sys_vmodel")]
|
[SugarTable("sys_vmodel")]
|
||||||
public partial class Vmodel : Entity
|
public partial class Vmodel : Entity
|
||||||
{
|
{
|
||||||
|
public const string MAIN_ALIES = "m";
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -146,7 +148,8 @@ public partial class Vmodel : Entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion Properties
|
#endregion Properties
|
||||||
|
//private Dictionary<string, string>? _mapField2Prop = null;
|
||||||
|
//private Dictionary<string, string>? _mapProp2Field = null;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过实体创建模型
|
/// 通过实体创建模型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -351,11 +354,30 @@ public partial class Vmodel : Entity
|
|||||||
if (filter == null) return wheres;
|
if (filter == null) return wheres;
|
||||||
foreach (var item in filter)
|
foreach (var item in filter)
|
||||||
{
|
{
|
||||||
|
VmDbProp? prop = null;
|
||||||
// TODO 按子表条件查询
|
// TODO 按子表条件查询
|
||||||
if (item.Key.Contains("."))
|
if (item.Key.Contains("."))
|
||||||
{
|
{
|
||||||
|
var codes = item.Key.Split('.');
|
||||||
|
if (codes.Length >= 2)
|
||||||
|
{
|
||||||
|
var navProp = navProps.FirstOrDefault(a => a.code == codes[0]);
|
||||||
|
if (navProp != null && navProp.naviModel != null)
|
||||||
|
{
|
||||||
|
var dbProp = navProp.naviModel.dbProps.FirstOrDefault(a => a.code == codes[1]);
|
||||||
|
if (dbProp != null)
|
||||||
|
{
|
||||||
|
prop = new VmDbProp();
|
||||||
|
prop.field = codes[0] + "." + dbProp.field;
|
||||||
|
prop.csType = dbProp.csType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prop = dbProps.FirstOrDefault(a => a.code == item.Key);
|
||||||
}
|
}
|
||||||
var prop = dbProps.FirstOrDefault(a => a.code == item.Key);
|
|
||||||
if (prop == null) continue;
|
if (prop == null) continue;
|
||||||
if (item.Value is JArray val)
|
if (item.Value is JArray val)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user