1.规范增量 SQL 文件命名

2.新增数据总线模块(未完成)
3.新增规则模块(未完成)
4.新增组织编码与外部系统组织编码映射关系表
5.补全 e 办单点登录回调逻辑
This commit is contained in:
chenbowen
2025-10-15 08:59:57 +08:00
parent 97bd87de55
commit c0dc0823b6
246 changed files with 11118 additions and 2749 deletions

View File

@@ -0,0 +1,105 @@
package com.zt.plat.module.databus.framework.integration.gateway.step.impl;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiStepDO;
import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiStepDefinition;
import com.zt.plat.module.databus.framework.integration.gateway.expression.ExpressionExecutor;
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.messaging.MessageHeaders;
import org.springframework.web.reactive.function.client.WebClient;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
class HttpStepHandlerTest {
private MockWebServer server;
private ExpressionExecutor expressionExecutor;
private HttpStepHandler handler;
@BeforeEach
void setUp() throws IOException {
server = new MockWebServer();
server.start();
expressionExecutor = Mockito.mock(ExpressionExecutor.class);
handler = new HttpStepHandler(WebClient.builder(), expressionExecutor);
}
@AfterEach
void tearDown() throws IOException {
server.shutdown();
}
@Test
void shouldForwardQueryParamsFromContextForGet() throws Exception {
server.enqueue(new MockResponse().setBody("{\"ok\":true}").setHeader("Content-Type", "application/json"));
ApiInvocationContext context = ApiInvocationContext.create();
context.getRequestQueryParams().put("id", "123");
context.setRequestBody(Collections.singletonMap("ignored", "value"));
ApiStepDO stepDO = new ApiStepDO();
stepDO.setId(1L);
stepDO.setType("HTTP");
stepDO.setTargetEndpoint("GET " + server.url("/orders"));
ApiStepDefinition stepDefinition = ApiStepDefinition.builder()
.step(stepDO)
.metadata(Collections.emptyMap())
.transforms(Collections.emptyList())
.build();
handler.build(null, stepDefinition).handle(context, new MessageHeaders(Collections.emptyMap()));
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertThat(request).isNotNull();
assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath()).isEqualTo("/orders?id=123");
assertThat(request.getBody().size()).isZero();
}
@Test
void shouldMergeExpressionBodyAndQueryParamsForPost() throws Exception {
server.enqueue(new MockResponse().setBody("{\"ok\":true}").setHeader("Content-Type", "application/json"));
ApiInvocationContext context = ApiInvocationContext.create();
context.setRequestBody(Collections.singletonMap("ignored", "value"));
ApiStepDO stepDO = new ApiStepDO();
stepDO.setId(2L);
stepDO.setType("HTTP");
stepDO.setTargetEndpoint("POST " + server.url("/payments"));
stepDO.setRequestMappingExpr("JSON::{\"body\": {\"amount\": 100}, \"query\": {\"token\": \"abc\"}}");
Map<String, Object> expressionResult = Map.of(
"body", Map.of("amount", 100),
"query", Map.of("token", "abc")
);
Mockito.when(expressionExecutor.evaluate(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(expressionResult);
ApiStepDefinition stepDefinition = ApiStepDefinition.builder()
.step(stepDO)
.metadata(Collections.emptyMap())
.transforms(Collections.emptyList())
.build();
handler.build(null, stepDefinition).handle(context, new MessageHeaders(Collections.emptyMap()));
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertThat(request).isNotNull();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getPath()).isEqualTo("/payments?token=abc");
assertThat(request.getHeader("Content-Type")).contains("application/json");
assertThat(request.getBody().readUtf8()).contains("\"amount\":100");
}
}

View File

@@ -0,0 +1,276 @@
package com.zt.plat.module.databus.service.gateway;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.test.core.ut.BaseDbUnitTest;
import com.zt.plat.framework.tenant.core.context.TenantContextHolder;
import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionSaveReqVO;
import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionStepSaveReqVO;
import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionTransformSaveReqVO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiDefinitionDO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyAuthDO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyRateLimitDO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiStepDO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiTransformDO;
import com.zt.plat.module.databus.dal.mysql.gateway.ApiDefinitionMapper;
import com.zt.plat.module.databus.dal.mysql.gateway.ApiPolicyAuthMapper;
import com.zt.plat.module.databus.dal.mysql.gateway.ApiPolicyRateLimitMapper;
import com.zt.plat.module.databus.dal.mysql.gateway.ApiStepMapper;
import com.zt.plat.module.databus.dal.mysql.gateway.ApiTransformMapper;
import com.zt.plat.module.databus.enums.gateway.ApiStatusEnum;
import com.zt.plat.module.databus.service.gateway.impl.ApiDefinitionServiceImpl;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.TestPropertySource;
import java.util.List;
import com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@Import({ApiDefinitionServiceImpl.class, ApiDefinitionServiceImplTest.JacksonTestConfiguration.class})
@TestPropertySource(properties = {
"spring.config.import=",
"config.server-addr=localhost:8848",
"config.group=DEFAULT_GROUP",
"config.namespace=public",
"config.username=nacos",
"config.password=nacos",
"spring.cloud.nacos.config.enabled=false",
"spring.cloud.nacos.discovery.enabled=false"
})
class ApiDefinitionServiceImplTest extends BaseDbUnitTest {
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiStepMapper apiStepMapper;
@Resource
private ApiTransformMapper apiTransformMapper;
@Resource
private ApiPolicyAuthMapper apiPolicyAuthMapper;
@Resource
private ApiPolicyRateLimitMapper apiPolicyRateLimitMapper;
@MockBean
private StringRedisTemplate stringRedisTemplate;
@TestConfiguration
static class JacksonTestConfiguration {
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
@AfterEach
void tearDown() {
TenantContextHolder.clear();
}
@Test
void testCreate_success() {
TenantContextHolder.setTenantId(1L);
Long authId = insertAuthPolicy();
Long rateId = insertRateLimitPolicy();
ApiDefinitionSaveReqVO reqVO = buildSaveReq(null, authId, rateId);
Long definitionId = apiDefinitionService.create(reqVO);
ApiDefinitionDO definition = apiDefinitionMapper.selectById(definitionId);
assertNotNull(definition);
assertEquals(reqVO.getApiCode(), definition.getApiCode());
assertEquals(reqVO.getVersion(), definition.getVersion());
assertEquals(1L, definition.getTenantId());
assertEquals(reqVO.getStatus(), definition.getStatus());
assertEquals(reqVO.getDescription(), definition.getDescription());
List<ApiStepDO> steps = apiStepMapper.selectByApiId(definitionId);
assertEquals(1, steps.size());
ApiStepDO step = steps.get(0);
assertEquals(1, step.getStepOrder());
assertEquals("HTTP", step.getType());
List<ApiTransformDO> apiLevelTransforms = apiTransformMapper.selectApiLevelTransforms(definitionId);
assertEquals(1, apiLevelTransforms.size());
assertEquals("REQUEST_PRE", apiLevelTransforms.get(0).getPhase());
List<ApiTransformDO> stepTransforms = apiTransformMapper.selectByStepId(step.getId());
assertEquals(1, stepTransforms.size());
assertEquals("RESPONSE_PRE", stepTransforms.get(0).getPhase());
}
@Test
void testCreate_duplicate() {
TenantContextHolder.setTenantId(1L);
ApiDefinitionDO definition = new ApiDefinitionDO();
definition.setTenantId(1L);
definition.setDeleted(false);
definition.setApiCode("order.create");
definition.setVersion("v1");
definition.setHttpMethod("POST");
definition.setUriPattern("/order/create");
definition.setStatus(ApiStatusEnum.ONLINE.getStatus());
apiDefinitionMapper.insert(definition);
ApiDefinitionSaveReqVO reqVO = buildSaveReq(null, null, null);
ServiceException exception = assertThrows(ServiceException.class, () -> apiDefinitionService.create(reqVO));
assertEquals(GatewayServiceErrorCodeConstants.API_DEFINITION_DUPLICATE.getCode(), exception.getCode());
}
@Test
void testUpdate_replaceSteps() {
TenantContextHolder.setTenantId(1L);
Long authId = insertAuthPolicy();
Long rateId = insertRateLimitPolicy();
ApiDefinitionDO definition = new ApiDefinitionDO();
definition.setTenantId(1L);
definition.setDeleted(false);
definition.setApiCode("order.update");
definition.setVersion("v1");
definition.setHttpMethod("POST");
definition.setUriPattern("/order/update");
definition.setStatus(ApiStatusEnum.ONLINE.getStatus());
apiDefinitionMapper.insert(definition);
ApiStepDO oldStep = new ApiStepDO();
oldStep.setApiId(definition.getId());
oldStep.setStepOrder(1);
oldStep.setType("HTTP");
oldStep.setTenantId(1L);
oldStep.setDeleted(false);
apiStepMapper.insert(oldStep);
ApiTransformDO oldTransform = new ApiTransformDO();
oldTransform.setApiId(definition.getId());
oldTransform.setStepId(oldStep.getId());
oldTransform.setPhase("REQUEST_PRE");
oldTransform.setExpressionType("JSON");
oldTransform.setExpression("{}");
oldTransform.setTenantId(1L);
oldTransform.setDeleted(false);
apiTransformMapper.insert(oldTransform);
ApiDefinitionSaveReqVO reqVO = buildSaveReq(definition.getId(), authId, rateId);
reqVO.setApiCode("order.update");
reqVO.setVersion("v2");
reqVO.getSteps().get(0).setStepOrder(2);
apiDefinitionService.update(reqVO);
List<ApiStepDO> steps = apiStepMapper.selectByApiId(definition.getId());
assertEquals(1, steps.size());
assertEquals(2, steps.get(0).getStepOrder());
List<ApiTransformDO> transforms = apiTransformMapper.selectByApiId(definition.getId());
assertThat(transforms)
.extracting(ApiTransformDO::getPhase)
.containsExactlyInAnyOrder("REQUEST_PRE", "RESPONSE_PRE");
}
@Test
void testDelete_success() {
TenantContextHolder.setTenantId(1L);
ApiDefinitionDO definition = new ApiDefinitionDO();
definition.setTenantId(1L);
definition.setDeleted(false);
definition.setApiCode("order.delete");
definition.setVersion("v1");
definition.setHttpMethod("DELETE");
definition.setUriPattern("/order/delete");
definition.setStatus(ApiStatusEnum.ONLINE.getStatus());
apiDefinitionMapper.insert(definition);
ApiStepDO step = new ApiStepDO();
step.setApiId(definition.getId());
step.setStepOrder(1);
step.setType("HTTP");
step.setTenantId(1L);
step.setDeleted(false);
apiStepMapper.insert(step);
ApiTransformDO transform = new ApiTransformDO();
transform.setApiId(definition.getId());
transform.setStepId(step.getId());
transform.setPhase("REQUEST_PRE");
transform.setExpressionType("JSON");
transform.setExpression("{}");
transform.setTenantId(1L);
transform.setDeleted(false);
apiTransformMapper.insert(transform);
apiDefinitionService.delete(definition.getId());
ApiDefinitionDO deleted = apiDefinitionMapper.selectById(definition.getId());
assertThat(deleted).isNull();
assertThat(apiStepMapper.selectByApiId(definition.getId())).isEmpty();
assertThat(apiTransformMapper.selectByApiId(definition.getId())).isEmpty();
}
private ApiDefinitionSaveReqVO buildSaveReq(Long id, Long authId, Long rateId) {
ApiDefinitionSaveReqVO reqVO = new ApiDefinitionSaveReqVO();
reqVO.setId(id);
reqVO.setApiCode("order.create");
reqVO.setVersion("v1");
reqVO.setHttpMethod("POST");
reqVO.setUriPattern("/order/create");
reqVO.setStatus(ApiStatusEnum.ONLINE.getStatus());
reqVO.setDescription("create order");
reqVO.setAuthPolicyId(authId);
reqVO.setRateLimitId(rateId);
ApiDefinitionTransformSaveReqVO apiTransform = new ApiDefinitionTransformSaveReqVO();
apiTransform.setPhase("REQUEST_PRE");
apiTransform.setExpressionType("JSON");
apiTransform.setExpression("{}");
reqVO.getApiLevelTransforms().add(apiTransform);
ApiDefinitionStepSaveReqVO step = new ApiDefinitionStepSaveReqVO();
step.setStepOrder(1);
step.setType("HTTP");
step.setTargetEndpoint("https://api.example.com/order");
ApiDefinitionTransformSaveReqVO stepTransform = new ApiDefinitionTransformSaveReqVO();
stepTransform.setPhase("RESPONSE_PRE");
stepTransform.setExpressionType("JSON");
stepTransform.setExpression("{}");
step.getTransforms().add(stepTransform);
reqVO.getSteps().add(step);
return reqVO;
}
private Long insertAuthPolicy() {
ApiPolicyAuthDO policy = new ApiPolicyAuthDO();
policy.setName("auth");
policy.setType("BASIC");
policy.setConfig("{}");
policy.setTenantId(1L);
policy.setDeleted(false);
apiPolicyAuthMapper.insert(policy);
return policy.getId();
}
private Long insertRateLimitPolicy() {
ApiPolicyRateLimitDO policy = new ApiPolicyRateLimitDO();
policy.setName("rate");
policy.setType("GLOBAL");
policy.setConfig("{}");
policy.setTenantId(1L);
policy.setDeleted(false);
apiPolicyRateLimitMapper.insert(policy);
return policy.getId();
}
}

View File

@@ -0,0 +1,42 @@
spring:
config:
import: ""
main:
lazy-initialization: true
banner-mode: off
datasource:
name: databus-unit-test
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value;
driver-class-name: org.h2.Driver
username: sa
password:
druid:
async-init: true
initial-size: 1
sql:
init:
schema-locations: classpath:/sql/create_tables.sql
data:
redis:
host: 127.0.0.1
port: 16379
database: 0
mybatis:
lazy-initialization: true
mybatis-plus:
global-config:
db-config:
id-type: AUTO
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
zt:
info:
base-package: com.zt.plat.module
env:
name: unit-test
config:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: public
group: DEFAULT_GROUP

View File

@@ -0,0 +1,5 @@
spring:
profiles:
active: unit-test
config:
import: ""

View File

@@ -0,0 +1,7 @@
DELETE FROM "databus_api_transform";
DELETE FROM "databus_api_step";
DELETE FROM "databus_api_definition";
DELETE FROM "databus_policy_auth";
DELETE FROM "databus_policy_rate_limit";
DELETE FROM "databus_policy_audit";
DELETE FROM "databus_api_flow_publish";

View File

@@ -0,0 +1,118 @@
CREATE TABLE IF NOT EXISTS databus_api_definition (
id BIGINT PRIMARY KEY,
api_code VARCHAR(255) NOT NULL,
uri_pattern VARCHAR(512),
http_method VARCHAR(16),
version VARCHAR(64),
status INT,
description VARCHAR(1024),
auth_policy_id BIGINT,
rate_limit_id BIGINT,
audit_policy_id BIGINT,
response_template CLOB,
cache_strategy VARCHAR(255),
updated_at TIMESTAMP,
grey_released BOOLEAN,
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_api_step (
id BIGINT PRIMARY KEY,
api_id BIGINT NOT NULL,
step_order INT,
parallel_group VARCHAR(64),
type VARCHAR(64),
target_endpoint VARCHAR(512),
request_mapping_expr VARCHAR(1024),
response_mapping_expr VARCHAR(1024),
transform_id BIGINT,
timeout BIGINT,
retry_strategy VARCHAR(255),
fallback_strategy VARCHAR(255),
condition_expr VARCHAR(1024),
stop_on_error BOOLEAN,
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_api_transform (
id BIGINT PRIMARY KEY,
api_id BIGINT,
step_id BIGINT,
phase VARCHAR(64),
expression_type VARCHAR(64),
expression VARCHAR(1024),
description VARCHAR(1024),
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_policy_auth (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(64),
config CLOB,
description VARCHAR(512),
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_policy_rate_limit (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(64),
config CLOB,
description VARCHAR(512),
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_policy_audit (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(64),
config CLOB,
description VARCHAR(512),
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);
CREATE TABLE IF NOT EXISTS databus_api_flow_publish (
id BIGINT PRIMARY KEY,
api_id BIGINT,
version VARCHAR(64),
status INT,
publish_time TIMESTAMP,
rollback_time TIMESTAMP,
tenant_id BIGINT,
create_time TIMESTAMP,
update_time TIMESTAMP,
creator VARCHAR(64),
updater VARCHAR(64),
deleted BOOLEAN
);