using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using Newtonsoft.Json;
using InterfaceForward.Application.Contracts.Dtos.Interface;
using InterfaceForward.Application.Helpers;
using InterfaceForward.Application.Services.Parameter;
using InterfaceForward.Domain.Shared;
using InterfaceForward.Domain.Shared.Enum;
using InterfaceForward.Repositories;
using InterfaceForward.Repositories.Interface.Entitys;
using InterfaceForward.Repositories.Interface.Services;
using InterfaceForward.Repositories.Interface.ValueObjects;
using InterfaceForward.Repositories.ServiceProvider.Entitys;
using Microsoft.AspNetCore.Mvc;
namespace InterfaceForward.Application.Services.Interface;
///
/// 接口服务(包括上下游接口)
///
[ApiExplorerSettings(GroupName = "接口服务")]
public class InterfaceService : ApplicationService
{
private ParameterService ParameterService => LazyServiceProvider.GetRequiredService();
private FormParameterService FormParameterService =>
LazyServiceProvider.GetRequiredService();
private IInterfaceRepository InterfaceRepository =>
LazyServiceProvider.GetRequiredService();
private IBasicRepository ServiceProviderRepository =>
LazyServiceProvider.GetRequiredService>();
private IBasicRepository InterfaceMapRepository =>
LazyServiceProvider.GetRequiredService>();
private InterfaceCategoryService InterfaceCategoryService =>
LazyServiceProvider.GetRequiredService();
///
/// 接口树形列表
///
///
///
[HttpPost("Interface/GetInterfaceTreeListAsync")]
public async Task>>
GetInterfaceTreeListAsync(InterfaceTreeListInputObjectValue input)
{
var res = await InterfaceRepository.GetInterfaceListAsync(input);
var tree = await InterfaceCategoryService.GetCategoryTree(false, res);
return tree;
}
///
/// 接口分页列表
///
///
///
[HttpPost("Interface/InterfacePaginatedList")]
public async Task>
InterfacePaginatedListAsync(InterfacePaginatedInputObjectValue input)
{
return await InterfaceRepository.GetInterfacePaginatedListAsync(input);
}
///
/// 获取接口下拉列表
///
/// 是否上游
/// 服务商id,上游接口传0
///
[HttpGet("Interface/GetInterfaceDropdownList")]
public async Task> GetInterfaceDropdownListAsync(
[Required] bool isUpstream, [Required] int serviceProviderId)
{
var res = await InterfaceRepository.GetListAsync(x => x.IsUpStream == isUpstream &&
x.ServiceProviderId == serviceProviderId,
x => new { x.Id, x.Code, x.Name });
return res.Select(x => new InterfaceDropdownListItem
{
Id = x.Id,
Name = x.Name,
Code = x.Code
}).ToList();
}
///
/// 获取接口明细
///
/// 接口id
///
[HttpGet("Interface/GetInterfaceById")]
public async Task GetInterfaceByIdAsync([Required] int id)
{
// 接口基本信息
var interfaceEntity = await InterfaceRepository.GetFirstAsync(x => x.Id == id);
if (interfaceEntity == null)
{
throw new BusinessException("接口不存在");
}
var res = ObjectMapper.Map(interfaceEntity);
// 构造参数
var paras = await InterfaceRepository.GetParameterListByIdAsync(id);
res.RequestBodyParameterList = !interfaceEntity.IsUpStream && interfaceEntity.ContentType is ContentType.Json or ContentType.Xml
? paras.Where(x => x.IsInPara && x.ParaKind == ParameterParaKind.Normal).ToList()
: [];
res.MappingBodyParameterList = !interfaceEntity.IsUpStream && interfaceEntity.ContentType is ContentType.None or ContentType.FormUrlEncoded
? paras.Where(x => x.IsInPara && x.ParaKind == ParameterParaKind.MappingBody).ToList()
: [];
// 出参树形列表
res.OutParameterList = paras.Where(x => !x.IsInPara).ToList();
// 表单列表
res.QueryFormParameterList = await FormParameterService.GetParameterListAsync(id, FieldPosition.Query);
res.HeaderFormParameterList = await FormParameterService.GetParameterListAsync(id, FieldPosition.Header);
res.BodyFormParameterList = await FormParameterService.GetParameterListAsync(id, FieldPosition.Body);
return res;
}
///
/// 保存接口
///
///
/// 用OptionType来区分操作目的,1:新增,2:删除,3:修改
///tips:新增时,参数id由调用者伪造,以表现层级关系,不能小于1,你必须真实的表达id和pid的关系
///
///
///
///
[HttpPost("Interface/SaveInterface")]
public async Task SaveInterfaceAsync(SaveInterfaceInput input)
{
NormalizeRootArrayName(GetInputParameterList(input));
NormalizeRootArrayName(input.OutParameterList);
ValidateBodyLists(input);
switch (input.OptionType)
{
case OptionType.新增:
return await CreateInterfaceAsync(input);
case OptionType.修改:
await UpdateInterfaceAsync(input);
break;
case OptionType.删除:
await BatchDeleteInterfaceByIdsAsync(input.IsUpStream, [input.Id ?? 0]);
break;
case OptionType.无:
break;
default:
throw new ArgumentOutOfRangeException();
}
return input.Id ?? 0;
}
///
/// 批量删除接口
///
/// 是否上游
/// 接口id集合
///
[HttpPost("Interface/BatchDeleteInterfaceByIds")]
public async Task BatchDeleteInterfaceByIdsAsync([FromQuery] bool isUpstream,
[FromBody] List ids)
{
using var tran = TransacationHelper.GetReadCommitted();
if (isUpstream)
{
// 删除接口
await InterfaceRepository.SoftDeleteAsync(x => x.IsUpStream && ids.Contains(x.Id));
// 删除接口映射
var mapIds = await InterfaceMapRepository.GetListAsync(x => ids.Contains(x.UpStreamId), x => x.Id);
if (mapIds.Count > 0)
{
await InterfaceMapRepository.SoftDeleteAsync(x => mapIds.Contains(x.Id));
}
}
else
{
// 删除接口
await InterfaceRepository.SoftDeleteAsync(x => !x.IsUpStream && ids.Contains(x.Id));
// 删除接口映射
var mapIds = await InterfaceMapRepository.GetListAsync(x => ids.Contains(x.DownStreamId), x => x.Id);
if (mapIds.Count > 0)
{
await InterfaceMapRepository.SoftDeleteAsync(x => mapIds.Contains(x.Id));
}
}
tran.Complete();
return true;
}
internal async Task CreateInterfaceAsync(SaveInterfaceInput input)
{
// 校验参数合法性
ParameterService.ValidateParameter(
JsonConvert.DeserializeObject>(
JsonConvert.SerializeObject(GetInputParameterList(input)))!);
ParameterService.ValidateParameter(
JsonConvert.DeserializeObject>(
JsonConvert.SerializeObject(input.OutParameterList))!);
/*
* 1.校验业务逻辑
* 接口名唯一性,接口简码唯一性,请求地址唯一性
* 2.清洗数据结构
* 参数合并,参数合法性,参数扁平化bfs
* 3.数据持久化
* 由于业务的观测粒度是保存行为(即取消不做任何数据保存),因此保持行为可以直接覆盖上次行为,注意事务
*/
#region 校验业务逻辑
if (input.Code.IsNullOrWhiteSpace())
{
input.Code = await GenerateInterfaceCode(input.ServiceProviderId);
}
if (await InterfaceRepository.IsAnyAsync(x =>
x.IsUpStream == input.IsUpStream && (x.Name.Equals(input.Name) || x.Code.Equals(input.Code)) &&
x.ServiceProviderId == input.ServiceProviderId))
{
throw new BusinessException($"接口名或code:{input.Name} 已存在");
}
#endregion
var interfaceEntity = ObjectMapper.Map(input);
// 开启事务
using var ts = TransacationHelper.GetReadCommitted();
// 持久化接口
var interfaceId = await InterfaceRepository.InsertReturnIdentityAsync(interfaceEntity);
// 持久化参数
await CreateInputParametersAsync(input, interfaceId);
await ParameterService.CreateParametersAsync(input.OutParameterList, interfaceId, false);
// 持久化表单参数
await CreateFormParametersAsync(input, interfaceId);
// 接口代码生成时并发兜底方案
if (await InterfaceRepository.CountAsync(x => x.Code.Equals(interfaceEntity.Code)) > 1)
{
throw new BusinessException("接口代码生成异常,请稍后重试");
}
// 提交事务
ts.Complete();
return interfaceId;
}
private async Task GenerateInterfaceCode(int serviceProviderId)
{
// 系统生成,由服务商代码+3位数字组成(0-999按顺序生成)
// tips:唯一性
var entity = await ServiceProviderRepository.GetFirstAsync(x => x.Id == serviceProviderId);
if (entity == null) throw new BusinessException("服务商不存在,系统接口自己定义code谢谢配合");
string res = entity.Code;
// tips:要考虑并发
var code = await InterfaceRepository.GetCurrentCodeBySpId(serviceProviderId);
int count = 0;
if (!string.IsNullOrEmpty(code)) count = int.Parse(code.Substring(code.Length - 3, 3));
res += (count + 1).ToString().PadLeft(3, '0');
return res;
}
private async Task UpdateInterfaceAsync(SaveInterfaceInput input)
{
#region 校验业务逻辑
if (!await InterfaceRepository.IsAnyAsync(x => x.Id == input.Id))
{
throw new BusinessException("接口不存在");
}
if (await InterfaceRepository.IsAnyAsync(x =>
x.Name.Equals(input.Name) && x.Id != input.Id && x.IsUpStream == input.IsUpStream &&
x.ServiceProviderId == input.ServiceProviderId))
{
throw new BusinessException($"接口名:{input.Name} 已存在");
}
#endregion
Debug.Assert(input.Id != null, $"{input.Id} 不能为空");
var interfaceEntity = ObjectMapper.Map(input);
// 开启事务
using var ts = TransacationHelper.GetReadCommitted();
// step1、更新接口
await InterfaceRepository.UpdateAsync(interfaceEntity, x => new
{
x.Code,
x.IsUpStream,
x.ServiceProviderId,
x.CreateTime,
x.CreateUserId,
x.IsDeleted
});
// step2、维护参数(增删改)
await MaintainInputParametersAsync(input, (int)input.Id);
await ParameterService.MaintainParametersAsync(input.OutParameterList, (int)input.Id, false);
// step3、更新表单参数
await MaintainFormParametersAsync(input, interfaceEntity.Id);
// 提交事务
ts.Complete();
}
private static List GetInputParameterList(SaveInterfaceInput input)
{
if (input.IsUpStream) return input.InParameterList;
return input.ContentType switch
{
ContentType.Json or ContentType.Xml => input.RequestBodyParameterList,
ContentType.None or ContentType.FormUrlEncoded => input.MappingBodyParameterList,
_ => []
};
}
private static void NormalizeRootArrayName(List list)
{
list.ForEach(x =>
{
if (x.PId == 0 && x.Type == ParameterType.Array && x.Name == GlobalConst.RootParameterName)
x.Name = string.Empty;
});
}
private static void ValidateBodyLists(SaveInterfaceInput input)
{
if (input.IsUpStream)
{
if (input.RequestBodyParameterList.Count > 0 || input.MappingBodyParameterList.Count > 0 || input.BodyFormParameterList.Count > 0)
throw new BusinessException("上游接口只能维护 inParameterList");
return;
}
switch (input.ContentType)
{
case ContentType.None:
if (input.InParameterList.Count > 0 || input.RequestBodyParameterList.Count > 0 || input.BodyFormParameterList.Count > 0)
throw new BusinessException("None 下游接口只能维护 Query/Header/MappingBody/Response");
break;
case ContentType.Json:
case ContentType.Xml:
if (input.InParameterList.Count > 0 || input.MappingBodyParameterList.Count > 0 || input.BodyFormParameterList.Count > 0)
throw new BusinessException("Json/Xml 下游接口只能维护 requestBodyParameterList");
break;
case ContentType.FormUrlEncoded:
if (input.InParameterList.Count > 0 || input.RequestBodyParameterList.Count > 0)
throw new BusinessException("FormUrlEncoded 下游接口只能维护 bodyFormParameterList 和 mappingBodyParameterList");
break;
case ContentType.FormData:
throw new BusinessException("暂时不支持该ContentType");
default:
throw new ArgumentOutOfRangeException();
}
}
private async Task CreateInputParametersAsync(SaveInterfaceInput input, int interfaceId)
{
if (input.IsUpStream)
{
await ParameterService.CreateParametersAsync(input.InParameterList, interfaceId, true);
return;
}
switch (input.ContentType)
{
case ContentType.None:
await ParameterService.CreateParametersAsync(input.MappingBodyParameterList, interfaceId, true,
ParameterParaKind.MappingBody);
break;
case ContentType.Json:
case ContentType.Xml:
await ParameterService.CreateParametersAsync(input.RequestBodyParameterList, interfaceId, true);
break;
case ContentType.FormUrlEncoded:
await ParameterService.CreateParametersAsync(input.MappingBodyParameterList, interfaceId, true,
ParameterParaKind.MappingBody);
break;
}
}
private async Task MaintainInputParametersAsync(SaveInterfaceInput input, int interfaceId)
{
if (input.IsUpStream)
{
await ParameterService.MaintainParametersAsync(input.InParameterList, interfaceId, true);
return;
}
switch (input.ContentType)
{
case ContentType.None:
await ParameterService.MaintainParametersAsync(input.MappingBodyParameterList, interfaceId, true,
ParameterParaKind.MappingBody);
break;
case ContentType.Json:
case ContentType.Xml:
await ParameterService.MaintainParametersAsync(input.RequestBodyParameterList, interfaceId, true);
break;
case ContentType.FormUrlEncoded:
await ParameterService.MaintainParametersAsync(input.MappingBodyParameterList, interfaceId, true,
ParameterParaKind.MappingBody);
break;
}
}
private async Task CreateFormParametersAsync(SaveInterfaceInput input, int interfaceId)
{
await FormParameterService.CreateParametersAsync(input.QueryFormParameterList, interfaceId, FieldPosition.Query);
await FormParameterService.CreateParametersAsync(input.HeaderFormParameterList, interfaceId, FieldPosition.Header);
if (!input.IsUpStream && input.ContentType == ContentType.FormUrlEncoded)
await FormParameterService.CreateParametersAsync(input.BodyFormParameterList, interfaceId, FieldPosition.Body);
}
private async Task MaintainFormParametersAsync(SaveInterfaceInput input, int interfaceId)
{
await FormParameterService.MaintainParametersAsync(input.QueryFormParameterList, interfaceId, FieldPosition.Query);
await FormParameterService.MaintainParametersAsync(input.HeaderFormParameterList, interfaceId, FieldPosition.Header);
if (!input.IsUpStream && input.ContentType == ContentType.FormUrlEncoded)
await FormParameterService.MaintainParametersAsync(input.BodyFormParameterList, interfaceId, FieldPosition.Body);
}
}