Compare commits

..

9 Commits

Author SHA1 Message Date
zzh
0ded5da8dd 1.04
All checks were successful
Build and Deploy / deploy (push) Successful in 1m9s
1.04
2026-10-12 13:57:41 +08:00
d8ae8a76ba fix
All checks were successful
Build and Deploy / deploy (push) Successful in 48s
2026-10-12 10:47:29 +08:00
142e8ff441 feat:缓存默认7day
All checks were successful
Build and Deploy / deploy (push) Successful in 48s
2026-10-09 11:43:53 +08:00
fdcc6c56f8 feat:去掉定制化接口登录检测
All checks were successful
Build and Deploy / deploy (push) Successful in 47s
2026-09-30 14:40:04 +08:00
25d0f2f778 local dev
All checks were successful
Build and Deploy / deploy (push) Successful in 51s
2026-09-28 15:35:15 +08:00
9016dc4116 cicd
All checks were successful
Build and Deploy / deploy (push) Successful in 45s
2026-09-22 15:50:02 +08:00
3f3da5bd37 test
Some checks failed
Build and Deploy / deploy (push) Failing after 22s
2026-09-21 15:59:36 +08:00
f0f648fb79 test
Some checks failed
Build and Deploy / deploy (push) Failing after 2m33s
2026-09-21 15:44:08 +08:00
db2cea4c8b init test 2026-09-21 15:42:49 +08:00
8 changed files with 126 additions and 26 deletions

View File

@ -1,9 +1,10 @@
# 复制到 itfx 仓库 .gitea/workflows/deploy.yml
# push master → prod;push test → test
name: Build and Deploy name: Build and Deploy
on: on:
push: push:
branches: [master, main] branches: [master, test]
workflow_dispatch:
env: env:
CSPROJ: src/InterfaceForward.Api/InterfaceForward.Api.csproj CSPROJ: src/InterfaceForward.Api/InterfaceForward.Api.csproj
@ -20,15 +21,47 @@ jobs:
- name: Build and deploy - name: Build and deploy
run: | run: |
set -euxo pipefail set -euxo pipefail
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml REF="${{ gitea.ref_name }}"
TAG="${{ gitea.sha }}" case "$REF" in
IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}" test) TARGET=test ;;
master) TARGET=prod ;;
*) echo "unsupported branch: $REF"; exit 1 ;;
esac
SHA="${{ gitea.sha }}"
REPO="${{ gitea.repository }}" REPO="${{ gitea.repository }}"
REPO="${REPO:-${{ github.repository }}}"
test -n "$REPO" && [[ "$REPO" == */* ]] test -n "$REPO" && [[ "$REPO" == */* ]]
echo "REPO=$REPO TAG=$TAG host=$(hostname)"
case "$TARGET" in
test) TAG="test-${SHA}" ;;
*) TAG="${SHA}" ;;
esac
IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}"
echo "TARGET=$TARGET REF=$REF REPO=$REPO TAG=$TAG host=$(hostname)"
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
unset NAMESPACE
cd /data/k3s cd /data/k3s
./scripts/ci-publish-container.sh "$REPO" "${{ env.CSPROJ }}" \ ./scripts/ci-publish-container.sh "$REPO" "${{ env.CSPROJ }}" \
"${{ env.IMAGE_NAME }}" "$TAG" "$TAG" "${{ env.IMAGE_NAME }}" "$TAG" "$SHA"
case "$TARGET" in
test)
export KUBECONFIG=/etc/rancher/k3s/k3s-test.yaml
export NAMESPACE=test
export MANIFEST_ROOT=/data/k3s-test
;;
*)
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
export NAMESPACE=prod
export MANIFEST_ROOT=/data/k3s
;;
esac
if [[ ! -f "$KUBECONFIG" ]]; then
echo "FAIL: missing KUBECONFIG=$KUBECONFIG host=$(hostname) uid=$(id -u)"
ls -la /etc/rancher/k3s/ /root/.kube/ 2>/dev/null || true
exit 1
fi
./scripts/ci-deploy-api.sh "${{ env.DEPLOY_NAME }}" \ ./scripts/ci-deploy-api.sh "${{ env.DEPLOY_NAME }}" \
"${{ env.CONTAINER_NAME }}" "$IMG" "${{ env.CONTAINER_NAME }}" "$IMG"

View File

@ -3,8 +3,9 @@ using Serilog;
var builder = WebApplication.CreateSlimBuilder(args); var builder = WebApplication.CreateSlimBuilder(args);
var configuration = builder.Configuration; var configuration = builder.Configuration;
builder.Configuration.AddConsul("idwms/common", source => source.ReloadOnChange = true); var prefix = builder.Environment.EnvironmentName == "Development" ? "dev" : "idwms";
builder.Configuration.AddConsul("idwms/itfxapi", source => source.ReloadOnChange = true); builder.Configuration.AddConsul($"{prefix}/common", source => source.ReloadOnChange = true);
builder.Configuration.AddConsul($"{prefix}/itfxapi", source => source.ReloadOnChange = true);
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration) .ReadFrom.Configuration(configuration)
.CreateLogger(); .CreateLogger();

View File

@ -1,7 +1,7 @@
{ {
"UseLocalConfigs": false, "UseLocalConfigs": false,
"Consul": { "Consul": {
"Address": "https://consul.nanafa.com", "Address": "http://test.ideship.com:30850",
"Token": "97217125-df0b-5c0a-dbfc-8885def5e471" "Token": "c0aea902-dca0-ac29-299b-a0d47e5402aa"
} }
} }

View File

@ -421,7 +421,8 @@ public class DefaultForwardFlow : IForwardFlow, ITransientDependency
Dictionary<string, string> nameValueCollection; Dictionary<string, string> nameValueCollection;
if (context.FormFieldList.Count != 0) if (context.FormFieldList.Count != 0)
{ {
var field = context.FormFieldList.FirstOrDefault(x => x.Value == GlobalConst.FromMappingBody); var field = context.FormFieldList.FirstOrDefault(x =>
x.Value is GlobalConst.FromMappingBody or GlobalConst.FromBody);
if (field != null) field.Value = body; if (field != null) field.Value = body;
nameValueCollection = context.FormFieldList.ToDictionary(x => x.Name, x => x.Value); nameValueCollection = context.FormFieldList.ToDictionary(x => x.Name, x => x.Value);
} }
@ -532,17 +533,18 @@ public class DefaultForwardFlow : IForwardFlow, ITransientDependency
// Query / Header / 表单 // Query / Header / 表单
foreach (var item in context.QueryFieldList) foreach (var item in context.QueryFieldList)
{ {
item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description); item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description, keepMappingBodyPlaceholder: false);
} }
foreach (var item in context.HeaderFieldList) foreach (var item in context.HeaderFieldList)
{ {
item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description); item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description, keepMappingBodyPlaceholder: false);
} }
foreach (var item in context.FormFieldList) foreach (var item in context.FormFieldList)
{ {
item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description); // FormUrlEncoded 的 Body KV 保留占位符,交由 SetContentType 用序列化后的 TargetInterfaceInput 填充
item.Value = ReplaceFormPlaceholder(item.Name, item.Value, item.Description, keepMappingBodyPlaceholder: true);
} }
// 绑定body部分的字段 // 绑定body部分的字段
@ -578,7 +580,7 @@ public class DefaultForwardFlow : IForwardFlow, ITransientDependency
} }
} }
string ReplaceFormPlaceholder(string name, string value, string? description) string ReplaceFormPlaceholder(string name, string value, string? description, bool keepMappingBodyPlaceholder)
{ {
return value switch return value switch
{ {
@ -589,9 +591,11 @@ public class DefaultForwardFlow : IForwardFlow, ITransientDependency
GlobalConst.DateTime => TurnDate(now, description), GlobalConst.DateTime => TurnDate(now, description),
GlobalConst.Url => context.TargetInterface.RequestAddress, GlobalConst.Url => context.TargetInterface.RequestAddress,
GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(name) ?? string.Empty, GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(name) ?? string.Empty,
GlobalConst.FromMappingBody => context.TargetInterface.ContentType == ContentType.FormUrlEncoded // Body 表单在 FormUrlEncoded 时保留占位符;Query/Header 必须立刻展开,否则会原样发出
? GlobalConst.FromMappingBody GlobalConst.FromMappingBody or GlobalConst.FromBody =>
: JsonConvert.SerializeObject(context.OriginalInterfaceMappedInput), keepMappingBodyPlaceholder && context.TargetInterface.ContentType == ContentType.FormUrlEncoded
? GlobalConst.FromMappingBody
: JsonConvert.SerializeObject(context.OriginalInterfaceMappedInput),
_ => value _ => value
}; };
} }

View File

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -7,6 +8,7 @@ namespace InterfaceForward.Application.Services.Customized;
[ApiExplorerSettings(GroupName = "接口转发服务")] [ApiExplorerSettings(GroupName = "接口转发服务")]
[TypeFilter(typeof(ShopeeForwardAuthorizeFilter))] [TypeFilter(typeof(ShopeeForwardAuthorizeFilter))]
[TypeFilter(typeof(ShopeeForwardExceptionFilter))] [TypeFilter(typeof(ShopeeForwardExceptionFilter))]
[AllowAnonymous]
public class Shopee(InterfaceForwardCommon forwardCommon): ApplicationService public class Shopee(InterfaceForwardCommon forwardCommon): ApplicationService
{ {
/// <summary> /// <summary>

View File

@ -108,10 +108,7 @@ public class InterfaceService : ApplicationService
// 构造参数 // 构造参数
var paras = await InterfaceRepository.GetParameterListByIdAsync(id); var paras = await InterfaceRepository.GetParameterListByIdAsync(id);
// 入参树形列表
res.InParameterList = interfaceEntity.IsUpStream
? paras.Where(x => x.IsInPara && x.ParaKind == ParameterParaKind.Normal).ToList()
: [];
res.RequestBodyParameterList = !interfaceEntity.IsUpStream && interfaceEntity.ContentType is ContentType.Json or ContentType.Xml res.RequestBodyParameterList = !interfaceEntity.IsUpStream && interfaceEntity.ContentType is ContentType.Json or ContentType.Xml
? paras.Where(x => x.IsInPara && x.ParaKind == ParameterParaKind.Normal).ToList() ? paras.Where(x => x.IsInPara && x.ParaKind == ParameterParaKind.Normal).ToList()
: []; : [];

View File

@ -26,9 +26,9 @@ public class InterfaceForwardCommon(
: ITransientDependency : ITransientDependency
{ {
/// <summary> /// <summary>
/// 缓存数据:映射,账户 默认过期时间 /// 缓存数据:映射,账户 默认过期时间s
/// </summary> /// </summary>
public static readonly int DefaultCacheTtl = int.MaxValue; public static readonly int DefaultCacheTtl = 7 * 24 * 60 * 60;
private readonly Dictionary<string, IForwardFlow> _flows = new(); private readonly Dictionary<string, IForwardFlow> _flows = new();

View File

@ -96,6 +96,69 @@ public class DefaultForwardFlowTests
Assert.Equal("{\"orderId\":\"A001\"}", Assert.Single(context.QueryFieldList).Value); Assert.Equal("{\"orderId\":\"A001\"}", Assert.Single(context.QueryFieldList).Value);
} }
[Fact]
public void ReplacePlaceholder_ShouldExpandQueryFromMappingBodyForFormUrlEncoded()
{
var flow = new TestForwardFlow();
var context = new ForwardCoreContext
{
TargetInterface = new InterfaceDto
{
ContentType = ContentType.FormUrlEncoded,
RequestAddress = "https://example.com/openapi"
},
OriginalInterfaceMappedInput = new JObject { ["orderId"] = "A001" },
QueryFieldList =
[
new InterfaceFormFieldDto
{
Name = "bizData",
Value = GlobalConst.FromMappingBody
}
],
FormFieldList =
[
new InterfaceFormFieldDto
{
Name = "notifyBizEventDTO",
Value = GlobalConst.FromMappingBody
}
]
};
flow.ReplacePlaceholder(context);
Assert.Equal("{\"orderId\":\"A001\"}", Assert.Single(context.QueryFieldList).Value);
Assert.Equal(GlobalConst.FromMappingBody, Assert.Single(context.FormFieldList).Value);
}
[Fact]
public void ReplacePlaceholder_ShouldExpandLegacyFromBodyInQuery()
{
var flow = new TestForwardFlow();
var context = new ForwardCoreContext
{
TargetInterface = new InterfaceDto
{
ContentType = ContentType.FormUrlEncoded,
RequestAddress = "https://example.com/openapi"
},
OriginalInterfaceMappedInput = new JObject { ["orderId"] = "A001" },
QueryFieldList =
[
new InterfaceFormFieldDto
{
Name = "bizData",
Value = GlobalConst.FromBody
}
]
};
flow.ReplacePlaceholder(context);
Assert.Equal("{\"orderId\":\"A001\"}", Assert.Single(context.QueryFieldList).Value);
}
class TestForwardFlow : DefaultForwardFlow class TestForwardFlow : DefaultForwardFlow
{ {
public void SetContentTypeForTest(ForwardCoreContext context, HttpRequestMessage message) public void SetContentTypeForTest(ForwardCoreContext context, HttpRequestMessage message)