From 5ac381f4f15b391b5b531fb2910a7775532762a7 Mon Sep 17 00:00:00 2001
From: xiaolipro <2357729423@qq.com>
Date: Thu, 30 Oct 2025 17:20:08 +0800
Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=201.=20=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9E=E5=8F=82?=
=?UTF-8?q?=E6=95=B0=E7=B1=BB=E5=9E=8B=E6=8E=A7=E5=88=B6=202.=20=E4=BC=98?=
=?UTF-8?q?=E5=8C=96=E5=BC=82=E5=B8=B8=E6=97=A5=E5=BF=97=203.=20=E6=94=AF?=
=?UTF-8?q?=E6=8C=81shopee=E5=AE=9A=E5=88=B6=E5=8C=96=204.=20=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E5=8A=A8=E6=80=81=E8=B7=AF=E7=94=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../InterfaceForwardApiModule.cs | 1 +
src/InterfaceForward.Api/appsettings.json | 5 +-
.../Dtos/Interface/ParameterParseInput.cs | 2 +-
.../ForwardCore/ForwardCoreContext.cs | 5 ++
.../Filters/ExceptionNotifier.cs | 64 +++++++++++++-
.../Filters/ForwardExceptionFilter.cs | 4 +-
.../ForwardCore/DefaultForwardFlow.cs | 2 +-
.../Services/App/AppsService.cs | 2 +-
.../Services/Customized/Shopee.cs | 43 +++++++++
.../ShopeeForwardAuthorizeFilter.cs | 87 +++++++++++++++++++
.../ShopeeForwardExceptionFilter.cs | 79 +++++++++++++++++
.../Services/InterfaceForwardCommon.cs | 3 +-
.../Services/InterfaceForwardService.cs | 7 +-
.../Parameter/FixedParameterService.cs | 5 +-
14 files changed, 291 insertions(+), 18 deletions(-)
create mode 100644 src/InterfaceForward.Application/Services/Customized/Shopee.cs
create mode 100644 src/InterfaceForward.Application/Services/Customized/ShopeeForwardAuthorizeFilter.cs
create mode 100644 src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs
diff --git a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
index e48d7ac..0a50729 100644
--- a/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
+++ b/src/InterfaceForward.Api/InterfaceForwardApiModule.cs
@@ -5,6 +5,7 @@ using Fake.AspNetCore.Mvc.Filters;
using Fake.Autofac;
using Fake.Modularity;
using InterfaceForward.Application;
+using InterfaceForward.Application.Filters;
using InterfaceForward.Domain.Shared.Helpers;
namespace InterfaceForward.Api;
diff --git a/src/InterfaceForward.Api/appsettings.json b/src/InterfaceForward.Api/appsettings.json
index 6bf8e37..fa8dcb5 100644
--- a/src/InterfaceForward.Api/appsettings.json
+++ b/src/InterfaceForward.Api/appsettings.json
@@ -14,7 +14,10 @@
],
"WriteTo": [
{
- "Name": "Console"
+ "Name": "Console",
+ "Args": {
+ "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}"
+ }
}
]
},
diff --git a/src/InterfaceForward.Application.Contracts/Dtos/Interface/ParameterParseInput.cs b/src/InterfaceForward.Application.Contracts/Dtos/Interface/ParameterParseInput.cs
index a94b849..99e0063 100644
--- a/src/InterfaceForward.Application.Contracts/Dtos/Interface/ParameterParseInput.cs
+++ b/src/InterfaceForward.Application.Contracts/Dtos/Interface/ParameterParseInput.cs
@@ -8,7 +8,7 @@ namespace InterfaceForward.Application.Contracts.Dtos.Interface;
public class ParameterParseInput
{
///
- /// 参数解析类型(0:json,1:xml)
+ /// 参数解析类型(1:json,2:xml)
///
public ParameterParseTypeEnum ParameterParseType { get; set; }
diff --git a/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs b/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
index ab61fb7..6b65a94 100644
--- a/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
+++ b/src/InterfaceForward.Application.Contracts/ForwardCore/ForwardCoreContext.cs
@@ -50,6 +50,11 @@ public class ForwardCoreContext
/// 请求后置脚本
///
public string? PostfixScript { get; set; }
+
+ ///
+ /// 返回参数 映射后参数类型(默认Object)
+ ///
+ public ParameterType MappedParamType { get; set; }
public bool AddHeader(string key, string value)
{
diff --git a/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs b/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs
index 075a461..ffa0b49 100644
--- a/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs
+++ b/src/InterfaceForward.Application/Filters/ExceptionNotifier.cs
@@ -1,14 +1,70 @@
+using System.Text;
using Fake.DependencyInjection;
using Fake.ExceptionHandling;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
namespace InterfaceForward.Application.Filters;
-public class ExceptionNotifier: IExceptionNotifier, ITransientDependency
+public class ExceptionNotifier : IExceptionNotifier, ITransientDependency
{
- public Task NotifyAsync(ExceptionNotificationContext context)
+ public async Task NotifyAsync(ExceptionNotificationContext context)
{
- LogHelper.Error(context.Exception.ToString());
+ var httpContext = context.ServiceProvider.GetRequiredService().HttpContext;
+ if (httpContext is null)
+ {
+ LogHelper.Error($@"
+ |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff}
+ |-异常堆栈:{context.Exception} ");
+ }
+
+ var request = httpContext!.Request;
+
+ // 1. 读取请求路径
+ var path = request.Path; // 例如: /api/users
+ var queryString = request.QueryString; // 例如: ?id=123
+ var fullPath = $"{path}{queryString}";
+
+ // 2. 读取请求方法
+ var method = request.Method; // GET, POST, PUT, etc.
+
+ // 3. 读取请求头
+ var headers = request.Headers;
+
+ // 4. 读取 Body (重要:需要启用缓冲)
+ request.EnableBuffering();
- return Task.CompletedTask;
+ // 如果 Body 已经被其他中间件读取过,需要先重置:
+ request.Body.Position = 0;
+
+ string body;
+ using (var reader = new StreamReader(
+ request.Body,
+ encoding: Encoding.UTF8,
+ detectEncodingFromByteOrderMarks: false,
+ leaveOpen: true))
+ {
+ body = await reader.ReadToEndAsync();
+ request.Body.Position = 0; // 重置流位置,以便后续中间件可以读取
+ }
+
+ if (context.Exception is BusinessException)
+ {
+ LogHelper.Warn($@"
+ |-请求路径:{method} {fullPath}
+ |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff}
+ |-请求用户:{headers.Authorization}
+ |-请求参数:{body}
+ |-业务异常:{context.Exception}", true);
+ }
+ else
+ {
+ LogHelper.Error($@"
+ |-请求路径:{method} {fullPath}
+ |-报错时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss ffff}
+ |-请求用户:{headers.Authorization}
+ |-请求参数:{body}
+ |-异常堆栈:{context.Exception} ");
+ }
}
}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs b/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs
index 5e3adcf..b5d10d5 100644
--- a/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs
+++ b/src/InterfaceForward.Application/Filters/ForwardExceptionFilter.cs
@@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations;
using InterfaceForward.Application.Contracts;
using InterfaceForward.Application.Contracts.ForwardCore;
-using InterfaceForward.Application.Rabbit;
using InterfaceForward.Repositories.Log.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -13,8 +12,7 @@ namespace InterfaceForward.Application.Filters;
public class ForwardExceptionFilter(
ILogRepository logRepository,
- IOptions options,
- RabbitClient rabbitClient)
+ IOptions options)
: IAsyncExceptionFilter
{
private readonly InterfaceRelayOptions _options = options.Value;
diff --git a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
index 600198d..8b14ddc 100644
--- a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
+++ b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs
@@ -61,7 +61,7 @@ public class DefaultForwardFlow : IForwardFlow, ITransientDependency
public virtual async Task OutParamRestructure(ForwardCoreContext context, JToken response)
{
// 映射
- var mappedRes = JsonHelper.Restructure(response, context.OutParamTreeList);
+ var mappedRes = JsonHelper.Restructure(response, context.OutParamTreeList, context.MappedParamType);
// 非结构化
if (context.TargetInterface.ResponseContentType is ResponseContentType.csv or ResponseContentType.pdf
or ResponseContentType.Base64)
diff --git a/src/InterfaceForward.Application/Services/App/AppsService.cs b/src/InterfaceForward.Application/Services/App/AppsService.cs
index 4876914..18865b1 100644
--- a/src/InterfaceForward.Application/Services/App/AppsService.cs
+++ b/src/InterfaceForward.Application/Services/App/AppsService.cs
@@ -94,7 +94,7 @@ public class AppsService : ApplicationService
var redisKey = nameof(GetKeyAndSecret) + "_" + date;
var num = await RedisHelper.Client.IncrByAsync(redisKey, 1);
if (num > 999) throw new BusinessException("超过每月设计的数量");
- var key = "BSI" + date + num.ToString().PadLeft(3, '0');
+ var key = "W" + date + num.ToString().PadLeft(3, '0');
//创建一个StringBuilder对象存储密码
StringBuilder secret = new StringBuilder();
diff --git a/src/InterfaceForward.Application/Services/Customized/Shopee.cs b/src/InterfaceForward.Application/Services/Customized/Shopee.cs
new file mode 100644
index 0000000..3fdfbd9
--- /dev/null
+++ b/src/InterfaceForward.Application/Services/Customized/Shopee.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json.Linq;
+
+namespace InterfaceForward.Application.Services.Customized;
+
+[ApiExplorerSettings(GroupName = "接口转发服务")]
+[TypeFilter(typeof(ShopeeForwardAuthorizeFilter))]
+[TypeFilter(typeof(ShopeeForwardExceptionFilter))]
+public class Shopee(InterfaceForwardCommon forwardCommon): ApplicationService
+{
+ ///
+ /// 动态路由:上游接口code/服务商code/定制化后缀
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ // "services/ilh_shipment/create"
+ [HttpPost("{upStreamCode}/{serviceProviderCode}/{postfix}")]
+ public async Task ForwardWithSubscribeAsync(
+ [FromRoute] [Required] string upStreamCode,
+ [FromRoute] [Required] string serviceProviderCode,
+ [FromBody] [Required] JToken data)
+ {
+ var contextList = await forwardCommon.VerifyBusinessAndBuildForwardContexts(upStreamCode, serviceProviderCode);
+
+ var context = contextList.First();
+ context.OriginalInterfaceInput = contextList.Count == 1
+ ? data
+ : new JObject
+ {
+ [upStreamCode] = data
+ };
+
+ var res = contextList.Count == 1
+ ? await forwardCommon.InternalForwardAsync(context)
+ : await forwardCommon.InternalForward2Async(contextList);
+
+ return res;
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/Customized/ShopeeForwardAuthorizeFilter.cs b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardAuthorizeFilter.cs
new file mode 100644
index 0000000..634172e
--- /dev/null
+++ b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardAuthorizeFilter.cs
@@ -0,0 +1,87 @@
+using System.Text;
+using Fake.AspNetCore.Http;
+using InterfaceForward.Repositories;
+using InterfaceForward.Repositories.Interface.Entitys;
+using InterfaceForward.Repositories.Log.Services;
+using Microsoft.AspNetCore.Http.Extensions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Newtonsoft.Json;
+
+namespace InterfaceForward.Application.Services.Customized;
+
+public class ShopeeForwardAuthorizeFilter(
+ InterfaceForwardQuery interfaceForwardQuery,
+ ILogRepository logRepository,
+ IHttpClientInfoProvider httpClientInfoProvider,
+ IBasicRepository interfaceRepository)
+ : IAsyncActionFilter
+{
+ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ var start = DateTime.Now;
+ var log = logRepository.AppRequestLog;
+ log.RequestTime = start;
+
+ var httpContext = context.HttpContext;
+
+ log.IsApp = true;
+
+ log.ClientIP = httpClientInfoProvider.ClientIpAddress;
+ log.Address = httpContext.Request.GetDisplayUrl();
+
+ // body
+ //重置RequestBody的Position为0
+ context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
+ using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))
+ {
+ log.Content = await reader.ReadToEndAsync();
+ }
+
+ var headers = httpContext.Request.Headers;
+ log.Headers = JsonConvert.SerializeObject(headers);
+
+ // string? appKey = headers["appKey"], appSecret = headers["appSecret"];
+ // if (appKey.IsNullOrWhiteSpace() || appSecret.IsNullOrWhiteSpace())
+ // {
+ // throw new BusinessException(message: "应用授权失败,AppKey和AppSecret是必填的");
+ // }
+ //
+ // var app = await interfaceForwardQuery.FindAppAsync(appKey!, appSecret!);
+ // if (app == null)
+ // throw new BusinessException(message: "应用授权失败,请检查AppKey或AppSecret是否正确");
+
+ // 先写死的
+ var app = await interfaceForwardQuery.FindAppAsync("W2507080", "G1rA80mAfGovH7j53t5bIH425M4vKreT");
+ log.Name = app.Name;
+ if (!app.IsOnline)
+ throw new BusinessException(message: "应用已被禁用,请检查应用状态");
+
+ logRepository.AppId = app.Id;
+
+ var upStreamCode = httpContext.Request.Path.ToUriComponent().Split('/')[1];
+ var systemInterface = await interfaceRepository.GetFirstAsync(x => x.Code == upStreamCode,
+ x => new { x.Code, x.Name });
+ if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
+ logRepository.AppRequestLog.InterfaceCode = systemInterface.Code;
+ logRepository.AppRequestLog.InterfaceName = systemInterface.Name;
+
+ var actionContext = await next();
+
+ if (actionContext.Exception == null)
+ {
+ log.Cost = (int)(DateTime.Now - start).TotalMilliseconds;
+
+ if (actionContext.Result is ObjectResult objectResult)
+ {
+ log.Response = JsonConvert.SerializeObject(objectResult.Value);
+ }
+ else
+ {
+ log.Response = actionContext.Result?.ToString()?? string.Empty;
+ }
+
+ _ = logRepository.WriteAppLog();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs
new file mode 100644
index 0000000..1a544f7
--- /dev/null
+++ b/src/InterfaceForward.Application/Services/Customized/ShopeeForwardExceptionFilter.cs
@@ -0,0 +1,79 @@
+using System.ComponentModel.DataAnnotations;
+using InterfaceForward.Application.Contracts;
+using InterfaceForward.Application.Contracts.ForwardCore;
+using InterfaceForward.Repositories.Log.Services;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+
+namespace InterfaceForward.Application.Services.Customized;
+
+public class ShopeeForwardExceptionFilter(
+ ILogRepository logRepository,
+ IOptions options)
+ : IAsyncExceptionFilter
+{
+ private readonly InterfaceRelayOptions _options = options.Value;
+
+ public Task OnExceptionAsync(ExceptionContext context)
+ {
+ if (context.ExceptionHandled) return Task.CompletedTask;
+
+ /*
+ * ServiceProviderRequestLog设计的意义是:
+ * 最大可能限度的保留请求服务商发生异常时附近的日志信息,以便于排查问题
+ */
+ var originMessage = bool.TryParse(context.HttpContext.Request.Headers["isReturnMessage"].ToString(),
+ out var isReturnMessage)
+ ? isReturnMessage ? logRepository.ServiceProviderRequestLog.Response : null
+ : null;
+
+ var res = new InterfaceRelayUnifyResultDto
+ {
+ Code = StatusCodes.Status400BadRequest.ToString(),
+ RequestId = logRepository.AppRequestLog.RequestId,
+ OriginalMessage = originMessage,
+ Attach = context.HttpContext.Request.Headers["attach"]
+ };
+
+ if (context.Exception is ValidationException errors) //验证异常
+ {
+ res.Msg = errors.Message;
+ context.Result = new JsonResult(res);
+ }
+ else if (context.Exception is BusinessException business) //业务异常
+ {
+ res.Msg = business.Message;
+ context.Result = new JsonResult(res);
+ }
+ else //未处理异常
+ {
+ logRepository.SendFeiShu(context.Exception);
+ res.Code = StatusCodes.Status500InternalServerError.ToString();
+ res.Msg = "服务器发生未处理异常";
+ context.Result = new JsonResult(res);
+ }
+
+ if (NeedPush(context, out var subscribeName))
+ {
+ // todo:要改成http回调的方式?
+ }
+
+ // 异常情况也要记录app日志
+ logRepository.AppRequestLog.Response = JsonConvert.SerializeObject(res);
+ _ = logRepository.WriteAppLog(context.Exception);
+ context.ExceptionHandled = true;
+ return Task.CompletedTask;
+ }
+
+ private bool NeedPush(ExceptionContext context, out string subscribeName)
+ {
+ subscribeName = null;
+ string interfaceCode = context.HttpContext.Request.Query["upStreamCode"];
+ subscribeName = context.HttpContext.Request.Headers["failedSubscribeName"];
+
+ return _options.FailedNeedPush.Contains(interfaceCode) && !subscribeName.IsNullOrWhiteSpace();
+ }
+}
\ No newline at end of file
diff --git a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
index 001e440..8e62980 100644
--- a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
+++ b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs
@@ -107,7 +107,8 @@ public class InterfaceForwardCommon(
FormFieldList = item.FormFieldList,
InParamTreeList = item.InParamTreeList,
PrefixScript = item.PrefixScript,
- PostfixScript = item.PostfixScript
+ PostfixScript = item.PostfixScript,
+ MappedParamType = cacheContext.OriginalInterface.MappedParamType
};
var token = context.FixedFieldList.FirstOrDefault(x => x.FieldValue == GlobalConst.Token);
if (token != default)
diff --git a/src/InterfaceForward.Application/Services/InterfaceForwardService.cs b/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
index 3416db3..efc99b7 100644
--- a/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
+++ b/src/InterfaceForward.Application/Services/InterfaceForwardService.cs
@@ -20,7 +20,6 @@ namespace InterfaceForward.Application.Services;
[TypeFilter(typeof(ForwardAuthorizeFilter))]
[TypeFilter(typeof(ForwardExceptionFilter))]
[AllowAnonymous]
-[Route("InterfaceForward")]
public class InterfaceForwardService : ApplicationService, IInterfaceForwardService
{
private readonly InterfaceForwardCommon _forwardCommon;
@@ -38,7 +37,7 @@ public class InterfaceForwardService : ApplicationService, IInterfaceForwardServ
_httpContextAccessor = httpContextAccessor;
}
- [HttpPost("BatchForwardThenPushToQueue")]
+ [HttpPost("InterfaceForward/BatchForwardThenPushToQueue")]
public async Task BatchForwardThenPushToQueueAsync(
[FromQuery] [Required] string upStreamCode,
[FromQuery] [Required] string serviceProviderCode,
@@ -111,7 +110,7 @@ public class InterfaceForwardService : ApplicationService, IInterfaceForwardServ
};
}
- [HttpPost("ForwardWithSubscribe")]
+ [HttpPost("InterfaceForward/ForwardWithSubscribe")]
public async Task ForwardWithSubscribeAsync(
[FromQuery] [Required] string upStreamCode,
[FromQuery] [Required] string serviceProviderCode,
@@ -147,7 +146,7 @@ public class InterfaceForwardService : ApplicationService, IInterfaceForwardServ
};
}
- [HttpPost("ReForward")]
+ [HttpPost("InterfaceForward/ReForward")]
public async Task ReForwardAsync(
[FromQuery] [Required] string upStreamCode,
[FromQuery] [Required] string serviceProviderCode,
diff --git a/src/InterfaceForward.Application/Services/Parameter/FixedParameterService.cs b/src/InterfaceForward.Application/Services/Parameter/FixedParameterService.cs
index bec5863..6699ff4 100644
--- a/src/InterfaceForward.Application/Services/Parameter/FixedParameterService.cs
+++ b/src/InterfaceForward.Application/Services/Parameter/FixedParameterService.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using Fake.Auditing;
using Fake.UnitOfWork;
using InterfaceForward.Application.Contracts.Dtos.ServiceProvider;
using InterfaceForward.Application.Helpers;
@@ -14,7 +15,7 @@ namespace InterfaceForward.Application.Services.Parameter;
/// 固定参数配置
///
[ApiExplorerSettings(GroupName = "参数服务")]
-public class FixedParameterService : ApplicationService
+public class FixedParameterService : ApplicationService, IAuditingEnabled
{
private readonly IFixedParameterRepository _fixedParameterRepository;
private readonly IServiceProviderRepository _serviceProviderRepository;
@@ -65,7 +66,7 @@ public class FixedParameterService : ApplicationService
///
///
[HttpPost("FixedParameter/MaintainParameter")]
- public async Task MaintainParameterAsync([FromBody] FixedParameterInput input)
+ public virtual async Task MaintainParameterAsync([FromBody] FixedParameterInput input)
{
// 校验服务商是否存在
if (!await _serviceProviderRepository.IsAnyAsync(x => x.Id == input.ServiceProviderId))