using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using InterfaceForward.Application.Contracts.Dtos.Interface;
using InterfaceForward.Application.Helpers;
using InterfaceForward.Domain.Shared;
using InterfaceForward.Domain.Shared.Enum;
using InterfaceForward.Repositories.Interface.Entitys;
using InterfaceForward.Repositories.Interface.Services;
using InterfaceForward.Repositories.Parameter.Services;
using InterfaceForward.Repositories.Parameter.VO;
using Microsoft.AspNetCore.Mvc;
namespace InterfaceForward.Application.Services.Parameter;
///
/// 参数服务
///
[ApiExplorerSettings(GroupName = "参数服务")]
public class ParameterService : ApplicationService
{
private readonly IInterfaceRepository _interfaceRepository;
private readonly IParameterRepository _parameterRepository;
///
/// 参数根id
///
public static readonly int ParameterRootId = 0;
///
public ParameterService(IInterfaceRepository interfaceRepository
, IParameterRepository parameterRepository)
{
_interfaceRepository = interfaceRepository;
_parameterRepository = parameterRepository;
}
///
/// 获取参数列表
///
/// 是否入参(true:入参,false:出参)
/// 接口id(不分上下游)
/// 只要叶子节点,默认false,返回所有节点
///
[HttpGet("Parameter/GetParameterList")]
public async Task> GetParameterListAsync([Required] bool isInPara,
[Required] int id, bool onlyLeafs = false)
{
var paras = await _interfaceRepository.GetParameterListByIdAsync(id, onlyLeafs);
return paras.Where(x => x.IsInPara == isInPara);
}
///
/// 保存参数
///
///
///
[HttpPost("Parameter/Save")]
public async Task SaveAsync(SaveParameterInput input)
{
if (!await _interfaceRepository.IsAnyAsync(x => x.Id == input.InterfaceId))
{
throw new BusinessException(message: "接口不存在");
}
return await MaintainParametersAsync(input.ParameterList, input.InterfaceId, input.IsInPara);
}
///
/// 保存参数序列
///
///
///
///
[HttpPost("Parameter/SaveSort")]
public async Task SaveSortAsync(SaveParameterSortInput input)
{
if (!await _interfaceRepository.IsAnyAsync(x => x.Id == input.InterfaceId))
{
throw new BusinessException(message: "接口不存在");
}
var entities = input.ParameterSortList.Select(x =>
{
if (x.Sort.IsNullOrWhiteSpace())
{
return new ParameterEntity()
{
Id = x.Id,
InterfaceId = input.InterfaceId
};
}
var segments = x.Sort.Split('.');
var newSegments = new string[segments.Length];
for (int i = 0; i < segments.Length; i++)
{
newSegments[i] = segments[i].PadLeft(3, '0');
}
return new ParameterEntity()
{
Id = x.Id,
Sort = newSegments.JoinAsString("."),
InterfaceId = input.InterfaceId
};
}).ToList();
await _parameterRepository.SaveSortAsync(entities);
return true;
}
///
/// 参数解析
///
///
///
[HttpPost("Parameter/Parse")]
public List Parse(
ParameterParseInput input)
{
switch (input.ParameterParseType)
{
case ParameterParseTypeEnum.Json:
return ParseJson(input.ParameterContent);
case ParameterParseTypeEnum.Xml:
return ParseXml(input.ParameterContent);
default:
throw new BusinessException(message: "暂不支持该格式解析");
}
}
///
/// 解析xml
///
/// xml字符串
///
/// xml无效
[ApiExplorerSettings(IgnoreApi = true)]
public List ParseXml(string str)
{
var doc = new XmlDocument();
string json;
try
{
doc.LoadXml(str);
json = JsonConvert.SerializeXmlNode(doc);
}
catch (Exception)
{
throw new BusinessException(message: "无法正常解析,请传入合法xml");
}
return ParseJson(json);
}
///
/// 解析json
///
/// 参数json字符串
///
/// json无效
[ApiExplorerSettings(IgnoreApi = true)]
public List ParseJson(string str)
{
JToken jtoken;
try
{
jtoken = JToken.Parse(str, new JsonLoadSettings
{
CommentHandling = CommentHandling.Load,
LineInfoHandling = LineInfoHandling.Ignore,
DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace
});
}
catch (Exception)
{
throw new BusinessException(message: "无法正常解析,请传入合法json");
}
Debug.Assert(jtoken != null, nameof(jtoken) + " != null");
int idx = 1, pid = 0;
return JToken2List(jtoken, ref idx, pid);
}
private List JToken2List(JToken jtoken, ref int idx, int pid)
{
if (jtoken is JObject obj) return Jobject2List(obj, ref idx, pid);
var annotation = GetAnnotation(jtoken);
if (jtoken is JArray arr)
{
idx++;
var res = JArray2List(arr, ref idx, 1);
var name = pid == 0 ? GlobalConst.RootParameterName : "arr";
res.Insert(0, new(1, 0, name, ParameterType.Array, annotation));
return res;
}
// value
var type = ParameterType.String;
if (jtoken.Type is JTokenType.Boolean) type = ParameterType.Boolean;
else if (jtoken.Type is JTokenType.Float) type = ParameterType.Float;
else if (jtoken.Type is JTokenType.Integer) type = ParameterType.Integer;
return new List { new(idx, pid, "val", type, annotation) };
}
private List Jobject2List(JObject jobject, ref int idx, int pid)
{
List res = new();
foreach (var cur in jobject.Properties())
{
// 不插入的
if (cur.Name.Equals("?xml") || cur.Name.StartsWith("@xmlns:")) continue;
var annotation = GetAnnotation(cur);
if (cur.Value is JObject obj)
{
res.Add(new ParameterParseOutput(idx++, pid, cur.Name, ParameterType.Object, annotation));
res.AddRange(Jobject2List(obj, ref idx, idx - 1));
}
else if (cur.Value is JArray arr)
{
res.Add(new ParameterParseOutput(idx++, pid, cur.Name, ParameterType.Array, annotation));
var v = JArray2List(arr, ref idx, idx - 1);
res.AddRange(v);
}
else
{
var type = ParameterType.String;
if (cur.Value.Type is JTokenType.Boolean) type = ParameterType.Boolean;
else if (cur.Value.Type is JTokenType.Float) type = ParameterType.Float;
else if (cur.Value.Type is JTokenType.Integer) type = ParameterType.Integer;
res.Add(new ParameterParseOutput(idx++, pid, cur.Name, type, annotation));
}
}
return res;
}
List JArray2List(JArray jarray, ref int idx, int pid)
{
var annotation = GetAnnotation(jarray);
List res = new();
// 空数组
if (!jarray.HasValues)
{
res.Add(new ParameterParseOutput(idx++, pid, "val", ParameterType.String, annotation));
return res;
}
// 数组的第一个结构就是所有元素的结构
var jToken = jarray[0];
annotation = GetAnnotation(jToken);
if (jToken is JArray arr)
{
res = res.Concat(JArray2List(arr, ref idx, idx - 1)).ToList();
}
else if (jToken is JObject obj)
{
res.Add(new ParameterParseOutput(idx++, pid, "obj", ParameterType.Object, annotation));
res = res.Concat(Jobject2List(obj, ref idx, idx - 1)).ToList();
}
else if (jToken is JValue val)
{
var type = ParameterType.String;
if (val.Type is JTokenType.Boolean) type = ParameterType.Boolean;
else if (val.Type is JTokenType.Float) type = ParameterType.Float;
else if (val.Type is JTokenType.Integer) type = ParameterType.Integer;
res.Add(new ParameterParseOutput(idx++, pid, "val", type, annotation));
}
return res;
}
static string GetAnnotation(JToken token)
{
return string.Empty;
}
internal async Task MaintainParametersAsync(List list, int interfaceId, bool isInPara)
{
#region 防止请求端没传入已存在的节点
var parameters = await _parameterRepository.GetParameterListByNameAsync(interfaceId, isInPara);
foreach (var item in parameters)
{
if (list.All(x => x.Id != item.Id))
list.Add(ObjectMapper.Map(item));
}
#endregion
ValidateParameter(
JsonConvert.DeserializeObject>(
JsonConvert.SerializeObject(list)));
// 开启事务
using var ts = TransacationHelper.GetReadCommitted();
// 创建
await CreateParametersAsync(list.Where(x => x.OptionType == OptionType.新增).ToList(), interfaceId, isInPara);
// 更新
await UpdateParametersAsync(list.Where(x => x.OptionType == OptionType.修改).ToList(), interfaceId, isInPara);
// 删除
await DeleteParametersAsync(list.Where(x => x.OptionType == OptionType.删除).Select(x => x.Id).ToList());
// 提交事务
ts.Complete();
return true;
}
///
/// bfs持久化参数
///
///
///
///
///
internal async Task CreateParametersAsync(List list, int interfaceId, bool isInPara)
{
var parameters =
await _parameterRepository.GetListAsync(x => x.InterfaceId == interfaceId && x.IsInPara == isInPara);
var tops = new List();
foreach (var item in list)
{
if (item.PId == ParameterRootId)
{
item.Alias = item.Name;
tops.Add(item);
continue;
}
if (list.All(x => x.Id != item.PId)) // 非顶级根
{
var entity = parameters.FirstOrDefault(x => x.Id == item.PId); // parent
if (entity == null)
{
continue; // 走空
}
// 参数层级关系用”.“进行区分,点击“新增”按钮后,取上一级的参数+“.”填充到新的参数名空格内(例:data.shipper.shipper_name)
item.Alias = entity.Alias + "." + item.Name;
tops.Add(item);
}
}
var queue = new Queue(tops);
while (queue.Count > 0)
{
var cur = queue.Dequeue();
var entity = ObjectMapper.Map(cur);
entity.Alias = cur.Alias;
entity.InterfaceId = interfaceId;
entity.IsInPara = isInPara;
if (!cur.Sort.IsNullOrWhiteSpace())
{
var segments = cur.Sort.Split('.');
var newSegments = new string[segments.Length];
for (int i = 0; i < segments.Length; i++)
{
newSegments[i] = segments[i].PadLeft(3, '0');
}
entity.Sort = newSegments.JoinAsString(".");
}
int id = await _parameterRepository.InsertReturnIdentityAsync(entity);
foreach (var item in list.Where(x => x.PId == cur.Id))
{
item.PId = id;
if (cur.Type == ParameterType.Array)
{
item.Alias = entity.Alias + "[*]";
}
else
{
// 参数层级关系用”.“进行区分,点击“新增”按钮后,取上一级的参数+“.”填充到新的参数名空格内(例:data.shipper.shipper_name)
item.Alias = entity.Alias + "." + item.Name;
}
queue.Enqueue(item);
}
}
}
private async Task DeleteParametersAsync(List ids)
{
if (ids.Count == 0) return;
await _parameterRepository.SoftDeleteAsync(x => ids.Contains(x.Id));
}
///
/// 给一棵树的任意几个节点 快速将以其为根的子树染色
///
///
///
///
private async Task UpdateParametersAsync(List list, int interfaceId, bool isInPara)
{
if (list.Count == 0) return;
Debug.Assert(!list.Any(x => x.Id < 1), "参数id均需大于0");
var parameters =
await _parameterRepository.GetListAsync(x => x.InterfaceId == interfaceId && x.IsInPara == isInPara);
var treeList = BuildTreeList(parameters, ParameterRootId);
var realUpdateObjs = GeneratorUpdateParameterEntities(treeList,
ObjectMapper.Map, List>(list));
await _parameterRepository.UpdateRangeAsync(realUpdateObjs, x => new
{
x.PId,
x.Alias,
x.Name,
x.CnName,
x.Type,
x.IsRequired,
x.Sort,
x.Description
});
}
private class ParameterNode
{
public ParameterEntity Val { get; set; }
public List Childrens { get; set; }
}
private List BuildTreeList(List list, int pid)
{
var res = new List();
foreach (var item in list.Where(x => x.PId == pid))
{
var node = new ParameterNode();
node.Val = item;
node.Childrens = BuildTreeList(list, item.Id);
res.Add(node);
}
return res;
}
List GeneratorUpdateParameterEntities(List treeList, List nodes)
{
var res = new List();
var queue = new Queue(treeList);
while (queue.Count > 0)
{
if (nodes.Count == 0) break;
var node = queue.Dequeue();
foreach (var subNode in node.Childrens)
{
queue.Enqueue(subNode);
}
var entity = nodes.FirstOrDefault(x => x.Id == node.Val.Id);
if (entity == null) continue;
nodes.Remove(entity);
entity.Alias = ReplaceLast(node.Val.Alias, node.Val.Name, entity.Name);
if (!node.Val.Sort.IsNullOrWhiteSpace())
{
var segments = node.Val.Sort.Split('.');
var newSegments = new string[segments.Length];
for (int i = 0; i < segments.Length; i++)
{
newSegments[i] = segments[i].PadLeft(3, '0');
}
entity.Sort = newSegments.JoinAsString(".");
}
res.Add(entity);
foreach (var subNode in node.Childrens) // 只刷一层
{
if (entity.Type == ParameterType.Array)
{
subNode.Val.Alias = entity.Alias + "[*]";
}
else
{
subNode.Val.Alias = entity.Alias + "." + subNode.Val.Name;
}
if (nodes.All(x => x.Id != subNode.Val.Id))
{
nodes.Add(subNode.Val);
}
}
}
return res;
}
private static string ReplaceLast(string str, string old, string cur,
StringComparison comparisonType = StringComparison.Ordinal)
{
ThrowHelper.ThrowIfNull(str, nameof(str));
if (old == cur) return str;
var pos = str.LastIndexOf(old, comparisonType);
if (pos < 0)
{
return str;
}
if (pos - 1 >= 0 && str[pos - 1] != '.') return str;
if (pos + old.Length < str.Length)
{
if (str[pos + old.Length] != '[') return str;
}
return string.Concat(str.AsSpan(0, pos), cur, str.AsSpan(pos + old.Length));
}
///
/// 参数校验
///
///
///
[HttpPost("Parameter/ValidateParameter")]
public bool ValidateParameter(List list)
{
if (list.Count < 1) return true;
list = list.Where(x => x.OptionType != OptionType.删除).ToList();
var tops = list.Where(x => x.PId == ParameterRootId).ToList();
tops.ForEach(x => x.Alias = x.Name);
var queue = new Queue(tops);
var set = new HashSet(list.Count);
while (queue.Count > 0)
{
var cur = queue.Dequeue();
// 校验唯一性
if (!set.Add(cur.Alias))
throw new BusinessException(message: $"不能存在相同路径的参数({cur.Name})");
if (cur.Type == ParameterType.Object)
{
if (list.All(x => x.PId != cur.Id))
throw new BusinessException(message: $"非法结构,空对象({cur.Name})");
}
else if (cur.Type == ParameterType.Array)
{
if (list.Count(x => x.PId == cur.Id) != 1)
throw new BusinessException(message: $"非法结构,数组元素(数组的子级)只能是一个对象或基本类型({cur.Name})");
}
else
{
if (list.Any(x => x.PId == cur.Id))
throw new BusinessException(message: $"非法结构,基本类型不能拥有下级({cur.Name})");
continue;
}
foreach (var item in list.Where(x => x.PId == cur.Id))
{
// 参数层级关系用”.“进行区分,点击“新增”按钮后,取上一级的参数+“.”填充到新的参数名空格内(例:data.shipper.shipper_name)
item.Alias = cur.Alias + "." + item.Name;
queue.Enqueue(item);
}
}
return true;
}
}