From bf8c250e9679286d823fbe720ed7014372d395f7 Mon Sep 17 00:00:00 2001 From: Antinew <2357729423@qq.com> Date: Sat, 23 Mar 2024 09:29:40 +0900 Subject: [PATCH] 1 --- .../Interface/InterfaceReturnConfigService.cs | 84 +++++ .../ServiceProviderInterfaceService.cs | 287 ++++++++++++++++++ .../Interface/SystemInterfaceService.cs | 279 +++++++++++++++++ .../InterfaceForwardApiModule.cs | 1 + .../InterfaceForwardDomainModule.cs | 3 +- 5 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 src/InterfaceForward.Api/Controllers/Interface/InterfaceReturnConfigService.cs create mode 100644 src/InterfaceForward.Api/Controllers/Interface/ServiceProviderInterfaceService.cs create mode 100644 src/InterfaceForward.Api/Controllers/Interface/SystemInterfaceService.cs diff --git a/src/InterfaceForward.Api/Controllers/Interface/InterfaceReturnConfigService.cs b/src/InterfaceForward.Api/Controllers/Interface/InterfaceReturnConfigService.cs new file mode 100644 index 0000000..eadd3cf --- /dev/null +++ b/src/InterfaceForward.Api/Controllers/Interface/InterfaceReturnConfigService.cs @@ -0,0 +1,84 @@ +using System.ComponentModel.DataAnnotations; +using Fake; +using Fake.AspNetCore; +using InterfaceForward.Domain.Aggregates.InterfaceAggregate; +using Microsoft.AspNetCore.Mvc; + +namespace SJZY.InterfaceRelay.Application.Services.Interface; + +/// +/// 接口返回配置 +/// +[ApiExplorerSettings(GroupName = "接口服务")] +public class InterfaceReturnConfigService : ApplicationService +{ + private readonly IInterfaceReturnConfigRepository _interfaceReturnConfigRepository; + private readonly IInterfaceRepository _interfaceRepository; + + /// + /// + /// + public InterfaceReturnConfigService( + IInterfaceReturnConfigRepository interfaceReturnConfigRepository + , IInterfaceRepository interfaceRepository) + { + _interfaceRepository = interfaceRepository; + _interfaceReturnConfigRepository = interfaceReturnConfigRepository; + } + + /// + /// 接口配置列表,配置类型传null则全查 + /// + /// 接口ID + /// + [HttpGet("InterfaceReturnConfig/GetList")] + public async Task> GetList([Required] int interfaceId) + { + return await _interfaceReturnConfigRepository.GetListAsync(interfaceId); + } + + /// + /// 更新或新增 + /// + /// 入参 + /// + [HttpPost("InterfaceReturnConfig/Save")] + public async Task SaveAsync(InterfaceReturnConfigInput input) + { + if (!await _interfaceRepository.IsAnyAsync(x => x.Id == input.InterfaceId)) + { + throw new BusinessException(message: "接口不存在"); + } + + // 开启事务 + using var ts = TransacationHelper.GetReadCommitted(); + + foreach (var item in input.Items) + { + var entity = + ObjectMapper + .Map(item); + entity.InterfaceId = input.InterfaceId; + entity.Value ??= ""; + switch (item.OptionType) + { + case OptionType.无: + break; + case OptionType.新增: + await _interfaceReturnConfigRepository.InsertOrUpdateAsync(entity, default); + break; + case OptionType.删除: + await _interfaceReturnConfigRepository.DeleteConfigByIdAsync(entity.Id); + break; + case OptionType.修改: + await _interfaceReturnConfigRepository.UpdateAsync(entity, ignoreAllNullColumns:true); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + ts.Complete(); + + return true; + } +} \ No newline at end of file diff --git a/src/InterfaceForward.Api/Controllers/Interface/ServiceProviderInterfaceService.cs b/src/InterfaceForward.Api/Controllers/Interface/ServiceProviderInterfaceService.cs new file mode 100644 index 0000000..cbcf9fa --- /dev/null +++ b/src/InterfaceForward.Api/Controllers/Interface/ServiceProviderInterfaceService.cs @@ -0,0 +1,287 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using Newtonsoft.Json; +using SJZY.Expand.ABP.Common.Enums; +using SJZY.InterfaceRelay.Application.Dtos.Interface; +using SJZY.InterfaceRelay.Application.Services.Parameter; +using SJZY.InterfaceRelay.Repository.Interface.Entitys; +using SJZY.InterfaceRelay.Repository.Interface.Services; +using SJZY.InterfaceRelay.Repository.Interface.ValueObjects; +using SJZY.InterfaceRelay.Repository.ServiceProvider.Entitys; + +namespace SJZY.InterfaceRelay.Application.Services.Interface; + +/// +/// 服务商接口 +/// +[ApiExplorerSettings(GroupName = "接口服务")] +public class ServiceProviderInterfaceService : ApplicationService +{ + private ParameterService ParameterService => LazyServiceProvider.LazyGetRequiredService(); + + private FormParameterService FormParameterService => + LazyServiceProvider.LazyGetRequiredService(); + + private IInterfaceRepository InterfaceRepository => + LazyServiceProvider.LazyGetRequiredService(); + + private IBasicRepository ServiceProviderRepository => + LazyServiceProvider.LazyGetRequiredService>(); + + private IBasicRepository InterfaceMapRepository => + LazyServiceProvider.LazyGetRequiredService>(); + + + /// + /// 服务商接口分页列表 + /// + /// + /// + [HttpPost("Interface/ServiceProviderInterfacePaginatedList")] + public async Task> + ServiceProviderInterfacePaginatedListAsync(ServiceProviderInterfacePaginatedInputObjectValue input) + { + return await InterfaceRepository.GetServiceProviderInterfacePaginatedListAsync(input); + } + + /// + /// 获取服务商接口下拉列表 + /// + /// 服务商id + /// + [HttpGet("Interface/GetServiceProviderInterfaceDropdownList")] + public async Task> GetServiceProviderInterfaceDropdownListAsync( + [Required] int serviceProviderId) + { + var res = await InterfaceRepository.GetListAsync(x => x.ServiceProviderId == serviceProviderId, + x => new { x.Id, x.Code, x.Name }); + + return res.Select(x => new ServiceProviderInterfaceDropdownListItem + { + Id = x.Id, + Name = x.Name, + Code = x.Code + }).ToList(); + } + + /// + /// 获取服务商接口明细 + /// + /// 服务商接口id + /// + [HttpGet("Interface/GetServiceProviderInterfaceById")] + public async Task GetServiceProviderInterfaceByIdAsync([Required] int id) + { + // 接口基本信息 + var interfaceEntity = await InterfaceRepository.GetFirstAsync(x => x.Id == id && !x.IsUpStream); + if (interfaceEntity == null) + { + throw new BusinessException(message: "服务商接口不存在"); + } + + var res = ObjectMapper.Map(interfaceEntity); + + // 构造参数 + var paras = await InterfaceRepository.GetParameterListByIdAsync(id); + // 入参树形列表 + res.InParameterList = paras.Where(x => x.IsInPara).ToList(); + // 出参树形列表 + res.OutParameterList = paras.Where(x => !x.IsInPara).ToList(); + + // 表单列表 + res.FormParameterList = await FormParameterService.GetParameterListAsync(id); + + return res; + } + + + /// + /// 保存服务商接口 + /// + /// + /// 用OptionType来区分操作目的,1:新增,2:删除,3:修改 + ///tips:新增时,参数id由调用者伪造,以表现层级关系,不能小于1,你必须真实的表达id和pid的关系 + /// + /// + /// + /// + [HttpPost("Interface/SaveServiceProviderInterface")] + public async Task SaveServiceProviderInterfaceAsync(ServiceProviderInterfaceInput input) + { + var res = new IdBaseDto(); + + switch (input.OptionType) + { + case OptionType.新增: + res.Id = await CreateServiceProviderInterfaceAsync(input); + break; + case OptionType.修改: + res.Id = await UpdateServiceProviderInterfaceAsync(input); + break; + case OptionType.删除: + await BatchDeleteServiceProviderInterfaceByIdsAsync(new List { input.Id ?? 0 }); + break; + default: + res.Id = res.Id; + break; + } + + return res; + } + + + /// + /// 批量删除服务商接口 + /// + /// 服务商接口id集合 + /// + [HttpPost("Interface/BatchDeleteServiceProviderInterfaceByIds")] + public async Task BatchDeleteServiceProviderInterfaceByIdsAsync([FromBody] List ids) + { + using var tran = TransacationHelper.GetReadCommitted(); + + // 删除接口 + 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 CreateServiceProviderInterfaceAsync(ServiceProviderInterfaceInput input) + { + // 校验参数合法性 + ParameterService.ValidateParameter( + JsonConvert.DeserializeObject>( + JsonConvert.SerializeObject(input.InParameterList))); + ParameterService.ValidateParameter( + JsonConvert.DeserializeObject>( + JsonConvert.SerializeObject(input.OutParameterList))); + + + /* + * 1.校验业务逻辑 + * 接口名唯一性,接口简码唯一性,请求地址唯一性 + * 2.清洗数据结构 + * 参数合并,参数合法性,参数扁平化bfs + * 3.数据持久化 + * 由于业务的观测粒度是保存行为(即取消不做任何数据保存),因此保持行为可以直接覆盖上次行为,注意事务 + */ + + #region 校验业务逻辑 + + if (await InterfaceRepository.IsAnyAsync(x => + !x.IsUpStream && x.Name.Equals(input.Name) && x.ServiceProviderId == input.ServiceProviderId)) + { + throw new BusinessException(message: $"接口名:{input.Name} 已存在"); + } + + #endregion + + var interfaceEntity = ObjectMapper.Map(input); + + interfaceEntity.IsUpStream = false; + + // 开启事务 + using var ts = TransacationHelper.GetReadCommitted(); + + // step1、生成接口编码 + interfaceEntity.Code = await GenerateServiceProviderInterfaceCode(input.ServiceProviderId); + // step2、持久化接口 + var interfaceId = await InterfaceRepository.InsertReturnIdentityAsync(interfaceEntity); + // 接口代码生成时并发兜底方案 + if (await InterfaceRepository.CountAsync(x => x.Code.Equals(interfaceEntity.Code)) > 1) + { + throw new BusinessException(message: "接口代码生成异常,请稍后重试"); + } + + // step3、持久化参数 + await ParameterService.CreateParametersAsync(input.InParameterList, interfaceId, true); + await ParameterService.CreateParametersAsync(input.OutParameterList, interfaceId, false); + + // step4、持久化表单参数 + if (input.FormParameterList.Count > 0) + { + await FormParameterService.CreateParametersAsync(input.FormParameterList, interfaceId); + } + + // 提交事务 + ts.Complete(); + + return interfaceId; + } + + private async Task GenerateServiceProviderInterfaceCode(int serviceProviderId) + { + // 系统生成,由服务商代码+3位数字组成(0-999按顺序生成) + // tips:唯一性 + var entity = await ServiceProviderRepository.GetFirstAsync(x => x.Id == serviceProviderId); + if (entity == null) throw new BusinessException(message: "服务商不存在"); + + 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 UpdateServiceProviderInterfaceAsync(ServiceProviderInterfaceInput input) + { + #region 校验业务逻辑 + + if (!await InterfaceRepository.IsAnyAsync(x => x.Id == input.Id)) + { + throw new BusinessException(message: "服务商接口不存在"); + } + + if (await InterfaceRepository.IsAnyAsync(x => + x.Name.Equals(input.Name) && x.Id != input.Id && !x.IsUpStream && + x.ServiceProviderId == input.ServiceProviderId)) + { + throw new BusinessException(message: $"接口名:{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 ParameterService.MaintainParametersAsync(input.InParameterList, (int)input.Id, true); + + await ParameterService.MaintainParametersAsync(input.OutParameterList, (int)input.Id, false); + + // step3、更新表单参数 + await FormParameterService.MaintainParametersAsync(input.FormParameterList, interfaceEntity.Id); + + // 提交事务 + ts.Complete(); + + return (int)input.Id; + } +} \ No newline at end of file diff --git a/src/InterfaceForward.Api/Controllers/Interface/SystemInterfaceService.cs b/src/InterfaceForward.Api/Controllers/Interface/SystemInterfaceService.cs new file mode 100644 index 0000000..0143847 --- /dev/null +++ b/src/InterfaceForward.Api/Controllers/Interface/SystemInterfaceService.cs @@ -0,0 +1,279 @@ +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using Newtonsoft.Json; +using SJZY.Expand.ABP.Common.Enums; +using SJZY.Expand.ABP.Core; +using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.InterfaceRelay.Application.Dtos.Interface; +using SJZY.InterfaceRelay.Application.Services.Parameter; +using SJZY.InterfaceRelay.Repository.Interface.Entitys; +using SJZY.InterfaceRelay.Repository.Interface.Services; +using SJZY.InterfaceRelay.Repository.Interface.ValueObjects; + +namespace SJZY.InterfaceRelay.Application.Services.Interface; + +/// +/// 系统接口 +/// +[ApiExplorerSettings(GroupName = "接口服务")] +public class SystemInterfaceService : ApplicationService +{ + #region ctor + + private readonly ParameterService _parameterService; + private readonly IInterfaceRepository _interfaceRepository; + private readonly IBasicRepository _interfaceMapRepository; + + /// + /// 系统接口下拉列表缓存key + /// + private const string SystemInterfaceDropdownCacheKey = "SystemInterfaceDropdownCacheKey"; + + /// + public SystemInterfaceService( + IInterfaceRepository interfaceRepository + , ParameterService parameterService + , IBasicRepository interfaceMapRepository) + { + _parameterService = parameterService; + _interfaceMapRepository = interfaceMapRepository; + _interfaceRepository = interfaceRepository; + } + + #endregion + + /// + /// 系统接口分页列表 + /// + /// + /// + [HttpPost("Interface/SystemInterfacePaginatedList")] + public async Task> + SystemInterfacePaginatedListAsync(SystemInterfacePaginatedInputObjectValue input) + { + return await _interfaceRepository.GetSystemInterfacePaginatedListAsync(input); + } + + /// + /// 获取系统接口明细 + /// + /// 系统接口id + /// + [HttpGet("Interface/GetSystemInterfaceById")] + public async Task GetSystemInterfaceByIdAsync([Required] int id) + { + // 接口基本信息 + var interfaceEntity = await _interfaceRepository.GetFirstAsync(x => x.Id == id && x.IsUpStream); + if (interfaceEntity == null) + { + throw new BusinessException(message: "系统接口不存在"); + } + + var res = ObjectMapper.Map(interfaceEntity); + + // 构造参数 + var paras = await _interfaceRepository.GetParameterListByIdAsync(id); + // 入参树形列表 + res.InParameterList = paras.Where(x => x.IsInPara).ToList(); + // 出参树形列表 + res.OutParameterList = paras.Where(x => !x.IsInPara).ToList(); + + return res; + } + + /// + /// 获取系统接口下拉列表 + /// + /// + [HttpGet("Interface/GetSystemInterfaceDropdownList")] + public async Task> GetSystemInterfaceDropdownListAsync() + { + var res = + (await RedisHelper.Client.GetAsync(SystemInterfaceDropdownCacheKey)) + .ToObject>(); + if (res == null) + { + res = await _interfaceRepository.GetSystemInterfaceDropdownListAsync(); + await RedisHelper.Client.SetAsync(SystemInterfaceDropdownCacheKey, res.ToJson(), 2 * 60 * 60); + } + + return res; + } + + /// + /// 保存系统接口 + /// + /// + /// 用OptionType来区分操作目的,1:新增,2:删除,3:修改 + ///注意!新增时,参数id由调用者伪造,以表现层级关系,不能小于1,你必须真实的表达id和pid的关系 + /// + /// + /// + /// + [HttpPost("Interface/SaveSystemInterface")] + public async Task SaveSystemInterfaceAsync(SystemInterfaceInput input) + { + var res = new IdBaseDto(); + + switch (input.OptionType) + { + case OptionType.新增: + res.Id = await CreateSystemInterfaceAsync(input); + break; + case OptionType.修改: + res.Id = await UpdateSystemInterfaceAsync(input); + break; + case OptionType.删除: + await BatchDeleteSystemInterfaceByIdsAsync(new List { input.Id ?? 0 }); + break; + default: + res.Id = res.Id; + break; + } + + // 干掉缓存 + await RedisHelper.Client.DelAsync(SystemInterfaceDropdownCacheKey); + return res; + } + + /// + /// 批量删除系统接口 + /// + /// 系统接口id集合 + /// + [HttpPost("Interface/BatchDeleteSystemInterfaceByIds")] + public async Task BatchDeleteSystemInterfaceByIdsAsync([FromBody] List ids) + { + using var tran = TransacationHelper.GetReadCommitted(); + + // 删除接口 + 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)); + } + // 删缓存 + await RedisHelper.Client.DelAsync(SystemInterfaceDropdownCacheKey); + + tran.Complete(); + + return true; + } + + #region private + + /// + /// 更新系统接口 + /// + /// + /// + private async Task UpdateSystemInterfaceAsync(SystemInterfaceInput input) + { + #region 校验业务逻辑 + + var entity = await _interfaceRepository.GetFirstAsync(x => x.Id == input.Id && x.IsUpStream); + + if (entity == null) + { + throw new BusinessException(message: "系统接口不存在"); + } + + if (await _interfaceRepository.IsAnyAsync(x => + x.Id != input.Id && x.IsUpStream && x.Name.Equals(input.Name))) + { + throw new BusinessException(message: $"接口名:{input.Name} 已存在"); + } + + if (await _interfaceRepository.IsAnyAsync( + x => x.Id != input.Id && x.IsUpStream && x.Code.Equals(input.Code))) + { + throw new BusinessException(message: "接口code已存在"); + } + + #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.IsUpStream, + x.ServiceProviderId, + x.CreateTime, + x.CreateUserId, + x.IsDeleted + }); + + // step2、维护参数(增删改) + await _parameterService.MaintainParametersAsync(input.InParameterList, (int)input.Id, true); + + await _parameterService.MaintainParametersAsync(input.OutParameterList, (int)input.Id, false); + + // 这里再做参数校验,回滚代价是不是有点大了。。 + + // 提交事务 + ts.Complete(); + + return (int)input.Id; + } + + private async Task CreateSystemInterfaceAsync(SystemInterfaceInput input) + { + // 校验参数合法性 + _parameterService.ValidateParameter( + JsonConvert.DeserializeObject>( + JsonConvert.SerializeObject(input.InParameterList))); + _parameterService.ValidateParameter( + JsonConvert.DeserializeObject>( + JsonConvert.SerializeObject(input.OutParameterList))); + + /* + * 1.校验业务逻辑 + * 接口名唯一性,接口简码唯一性,请求地址唯一性 + * 2.清洗数据结构 + * 参数合并,参数合法性,参数扁平化bfs + * 3.数据持久化 + * 由于业务的观测粒度是保存行为(即取消不做任何数据保存),因此保持行为可以直接覆盖上次行为,注意事务 + */ + + #region 校验业务逻辑 + + if (await _interfaceRepository.IsAnyAsync(x => x.IsUpStream && x.Name.Equals(input.Name))) + { + throw new BusinessException(message: $"接口名:{input.Name} 已存在"); + } + + if (await _interfaceRepository.IsAnyAsync(x => x.IsUpStream && x.Code.Equals(input.Code))) + { + throw new BusinessException(message: "接口code已存在"); + } + + #endregion + + var interfaceEntity = ObjectMapper.Map(input); + interfaceEntity.IsUpStream = true; + + // 开启事务 + using var ts = TransacationHelper.GetReadCommitted(); + // step1、持久化接口 + var interfaceId = await _interfaceRepository.InsertReturnIdentityAsync(interfaceEntity); + + // step2、持久化参数 + await _parameterService.CreateParametersAsync(input.InParameterList, interfaceId, true); + await _parameterService.CreateParametersAsync(input.OutParameterList, interfaceId, false); + + // 提交事务 + ts.Complete(); + + return interfaceId; + } + + #endregion +} \ No newline at end of file diff --git a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs index b8cceb5..1d0ac03 100644 --- a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs +++ b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs @@ -1,5 +1,6 @@ using Fake.AspNetCore; using Fake.Authorization; +using Fake.Helpers; using Fake.Modularity; using InterfaceForward.Api.Contract; using InterfaceForward.Api.Filters; diff --git a/src/InterfaceForward.Domain/InterfaceForwardDomainModule.cs b/src/InterfaceForward.Domain/InterfaceForwardDomainModule.cs index 5b3852b..e3842ce 100644 --- a/src/InterfaceForward.Domain/InterfaceForwardDomainModule.cs +++ b/src/InterfaceForward.Domain/InterfaceForwardDomainModule.cs @@ -1,8 +1,9 @@ using System; +using Fake.Modularity; namespace InterfaceForward.Domain { - public class InterfaceForwardDomainModule + public class InterfaceForwardDomainModule : FakeModule { } } \ No newline at end of file