diff --git a/sql/dm/e 办同步信息日志表结构.sql b/sql/dm/e 办同步信息日志表结构_20250917.sql
similarity index 100%
rename from sql/dm/e 办同步信息日志表结构.sql
rename to sql/dm/e 办同步信息日志表结构_20250917.sql
diff --git a/sql/dm/在线文档管理菜单.sql b/sql/dm/在线文档管理菜单_20250901.sql
similarity index 100%
rename from sql/dm/在线文档管理菜单.sql
rename to sql/dm/在线文档管理菜单_20250901.sql
diff --git a/sql/dm/在线文档管理表结构.sql b/sql/dm/在线文档管理表结构_20250901.sql
similarity index 100%
rename from sql/dm/在线文档管理表结构.sql
rename to sql/dm/在线文档管理表结构_20250901.sql
diff --git a/sql/dm/扩充用户表部门表名称字段,匹配e办数据.sql b/sql/dm/扩充用户表部门表名称字段,匹配e办数据_20250918.sql
similarity index 100%
rename from sql/dm/扩充用户表部门表名称字段,匹配e办数据.sql
rename to sql/dm/扩充用户表部门表名称字段,匹配e办数据_20250918.sql
diff --git a/sql/dm/新增业务附件表状态.sql b/sql/dm/新增业务附件表状态_20250925.sql
similarity index 100%
rename from sql/dm/新增业务附件表状态.sql
rename to sql/dm/新增业务附件表状态_20250925.sql
diff --git a/sql/dm/新增用户、组织机构源表结构并迁移.sql b/sql/dm/新增用户、组织机构源表结构并迁移_20250917.sql
similarity index 100%
rename from sql/dm/新增用户、组织机构源表结构并迁移.sql
rename to sql/dm/新增用户、组织机构源表结构并迁移_20250917.sql
diff --git a/sql/dm/新增用户与组织关系表并迁移.sql b/sql/dm/新增用户与组织关系表并迁移_20250717.sql
similarity index 100%
rename from sql/dm/新增用户与组织关系表并迁移.sql
rename to sql/dm/新增用户与组织关系表并迁移_20250717.sql
diff --git a/sql/dm/新增组织机构类型字段.sql b/sql/dm/新增组织机构类型字段_20250703.sql
similarity index 100%
rename from sql/dm/新增组织机构类型字段.sql
rename to sql/dm/新增组织机构类型字段_20250703.sql
diff --git a/sql/dm/统一外部网关菜单_20251010.sql b/sql/dm/统一外部网关菜单_20251010.sql
new file mode 100644
index 00000000..e7c36fe3
--- /dev/null
+++ b/sql/dm/统一外部网关菜单_20251010.sql
@@ -0,0 +1,31 @@
+
+-- 清理旧数据,确保脚本可重复执行
+DELETE FROM system_menu WHERE id IN (6500,6501,650101,650102,650103);
+
+-- 顶级目录(父级假定为 id=2 的系统管理目录)
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+) VALUES (
+ 6500, '统一外部网关', '', 1, 20, 1,
+ 'databus', 'ep:data-line', '', 0, NULL
+);
+
+-- API 门户页面
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+) VALUES (
+ 6501, 'API 门户', 'databus:gateway:query', 2, 1, 6500,
+ 'databus-gateway', 'ep:cpu', 'databus/gateway/index', 0, 'DatabusGateway'
+);
+
+-- 页面内操作按钮权限
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status
+) VALUES
+ (650101, 'API 列表', 'databus:gateway:query', 3, 1, 6501, '', '', '', 0),
+ (650102, 'API 调试', 'databus:gateway:invoke', 3, 2, 6501, '', '', '', 0),
+ (650103, '刷新定义', 'databus:gateway:refresh', 3, 3, 6501, '', '', '', 0);
+d
\ No newline at end of file
diff --git a/sql/dm/统一对外网关_20251010.sql b/sql/dm/统一对外网关_20251010.sql
new file mode 100644
index 00000000..b4c39768
--- /dev/null
+++ b/sql/dm/统一对外网关_20251010.sql
@@ -0,0 +1,241 @@
+/*
+ * Databus API portal schema for DM8
+ * Generated on 2025-10-10
+ */
+
+-- ----------------------------
+-- Table structure for databus_api_definition
+-- ----------------------------
+CREATE TABLE databus_api_definition (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ api_code VARCHAR(128) NOT NULL,
+ uri_pattern VARCHAR(256) NOT NULL,
+ http_method VARCHAR(16) NOT NULL,
+ version VARCHAR(32) NOT NULL,
+ status SMALLINT DEFAULT 0 NOT NULL,
+ description VARCHAR(512),
+ auth_policy_id BIGINT,
+ rate_limit_id BIGINT,
+ response_template CLOB,
+ cache_strategy VARCHAR(128),
+ updated_at DATETIME,
+ grey_released BIT DEFAULT '0' NOT NULL,
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE UNIQUE INDEX uk_databus_api_definition_code_ver ON databus_api_definition (tenant_id, api_code, version);
+CREATE INDEX idx_databus_api_definition_status ON databus_api_definition (tenant_id, status);
+CREATE INDEX idx_databus_api_definition_policy ON databus_api_definition (tenant_id, auth_policy_id, rate_limit_id);
+
+COMMENT ON TABLE databus_api_definition IS '统一外部 API 门户 - API 定义表';
+COMMENT ON COLUMN databus_api_definition.id IS '主键 ID';
+COMMENT ON COLUMN databus_api_definition.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_api_definition.api_code IS 'API 编码';
+COMMENT ON COLUMN databus_api_definition.uri_pattern IS '匹配路径模板';
+COMMENT ON COLUMN databus_api_definition.http_method IS 'HTTP 方法';
+COMMENT ON COLUMN databus_api_definition.version IS '版本号';
+COMMENT ON COLUMN databus_api_definition.status IS '发布状态';
+COMMENT ON COLUMN databus_api_definition.description IS '描述信息';
+COMMENT ON COLUMN databus_api_definition.auth_policy_id IS '认证策略 ID';
+COMMENT ON COLUMN databus_api_definition.rate_limit_id IS '限流策略 ID';
+COMMENT ON COLUMN databus_api_definition.response_template IS '响应模板 JSON';
+COMMENT ON COLUMN databus_api_definition.cache_strategy IS '缓存策略配置';
+COMMENT ON COLUMN databus_api_definition.updated_at IS '业务更新时间';
+COMMENT ON COLUMN databus_api_definition.grey_released IS '灰度发布标记';
+COMMENT ON COLUMN databus_api_definition.creator IS '创建者';
+COMMENT ON COLUMN databus_api_definition.create_time IS '创建时间';
+COMMENT ON COLUMN databus_api_definition.updater IS '更新者';
+COMMENT ON COLUMN databus_api_definition.update_time IS '更新时间';
+COMMENT ON COLUMN databus_api_definition.deleted IS '逻辑删除标记';
+
+-- ----------------------------
+-- Table structure for databus_api_flow_publish
+-- ----------------------------
+CREATE TABLE databus_api_flow_publish (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ api_id BIGINT NOT NULL,
+ release_tag VARCHAR(64),
+ snapshot CLOB,
+ status VARCHAR(32),
+ active BIT DEFAULT '0' NOT NULL,
+ description VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE INDEX idx_databus_api_flow_publish_api ON databus_api_flow_publish (tenant_id, api_id, active);
+
+COMMENT ON TABLE databus_api_flow_publish IS '统一外部 API 门户 - 发布记录表';
+COMMENT ON COLUMN databus_api_flow_publish.id IS '主键 ID';
+COMMENT ON COLUMN databus_api_flow_publish.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_api_flow_publish.api_id IS '关联的 API ID';
+COMMENT ON COLUMN databus_api_flow_publish.release_tag IS '发布批次标识';
+COMMENT ON COLUMN databus_api_flow_publish.snapshot IS '配置快照 JSON';
+COMMENT ON COLUMN databus_api_flow_publish.status IS '发布状态';
+COMMENT ON COLUMN databus_api_flow_publish.active IS '是否当前生效';
+COMMENT ON COLUMN databus_api_flow_publish.description IS '备注信息';
+COMMENT ON COLUMN databus_api_flow_publish.creator IS '创建者';
+COMMENT ON COLUMN databus_api_flow_publish.create_time IS '创建时间';
+COMMENT ON COLUMN databus_api_flow_publish.updater IS '更新者';
+COMMENT ON COLUMN databus_api_flow_publish.update_time IS '更新时间';
+COMMENT ON COLUMN databus_api_flow_publish.deleted IS '逻辑删除标记';
+
+-- ----------------------------
+-- Table structure for databus_policy_auth
+-- ----------------------------
+CREATE TABLE databus_policy_auth (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ name VARCHAR(128) NOT NULL,
+ type VARCHAR(64) NOT NULL,
+ config CLOB,
+ description VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE UNIQUE INDEX uk_databus_policy_auth_name ON databus_policy_auth (tenant_id, name);
+
+COMMENT ON TABLE databus_policy_auth IS '统一外部 API 门户 - 认证策略表';
+COMMENT ON COLUMN databus_policy_auth.id IS '主键 ID';
+COMMENT ON COLUMN databus_policy_auth.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_policy_auth.name IS '策略名称';
+COMMENT ON COLUMN databus_policy_auth.type IS '策略类型';
+COMMENT ON COLUMN databus_policy_auth.config IS '策略配置 JSON';
+COMMENT ON COLUMN databus_policy_auth.description IS '描述信息';
+COMMENT ON COLUMN databus_policy_auth.creator IS '创建者';
+COMMENT ON COLUMN databus_policy_auth.create_time IS '创建时间';
+COMMENT ON COLUMN databus_policy_auth.updater IS '更新者';
+COMMENT ON COLUMN databus_policy_auth.update_time IS '更新时间';
+COMMENT ON COLUMN databus_policy_auth.deleted IS '逻辑删除标记';
+
+-- ----------------------------
+-- Table structure for databus_policy_rate_limit
+-- ----------------------------
+CREATE TABLE databus_policy_rate_limit (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ name VARCHAR(128) NOT NULL,
+ type VARCHAR(64) NOT NULL,
+ config CLOB,
+ description VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE UNIQUE INDEX uk_databus_policy_rate_limit_name ON databus_policy_rate_limit (tenant_id, name);
+
+COMMENT ON TABLE databus_policy_rate_limit IS '统一外部 API 门户 - 限流策略表';
+COMMENT ON COLUMN databus_policy_rate_limit.id IS '主键 ID';
+COMMENT ON COLUMN databus_policy_rate_limit.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_policy_rate_limit.name IS '策略名称';
+COMMENT ON COLUMN databus_policy_rate_limit.type IS '策略类型';
+COMMENT ON COLUMN databus_policy_rate_limit.config IS '策略配置 JSON';
+COMMENT ON COLUMN databus_policy_rate_limit.description IS '描述信息';
+COMMENT ON COLUMN databus_policy_rate_limit.creator IS '创建者';
+COMMENT ON COLUMN databus_policy_rate_limit.create_time IS '创建时间';
+COMMENT ON COLUMN databus_policy_rate_limit.updater IS '更新者';
+COMMENT ON COLUMN databus_policy_rate_limit.update_time IS '更新时间';
+COMMENT ON COLUMN databus_policy_rate_limit.deleted IS '逻辑删除标记';
+
+-- ----------------------------
+-- Table structure for databus_api_step
+-- ----------------------------
+CREATE TABLE databus_api_step (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ api_id BIGINT NOT NULL,
+ step_order INT DEFAULT 0 NOT NULL,
+ parallel_group VARCHAR(64),
+ type VARCHAR(32) NOT NULL,
+ target_endpoint VARCHAR(512),
+ request_mapping_expr CLOB,
+ response_mapping_expr CLOB,
+ transform_id BIGINT,
+ timeout BIGINT,
+ retry_strategy CLOB,
+ fallback_strategy CLOB,
+ condition_expr CLOB,
+ stop_on_error BIT DEFAULT '0' NOT NULL,
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE INDEX idx_databus_api_step_api_order ON databus_api_step (tenant_id, api_id, parallel_group, step_order);
+
+COMMENT ON TABLE databus_api_step IS '统一外部 API 门户 - 编排步骤表';
+COMMENT ON COLUMN databus_api_step.id IS '主键 ID';
+COMMENT ON COLUMN databus_api_step.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_api_step.api_id IS '关联的 API ID';
+COMMENT ON COLUMN databus_api_step.step_order IS '执行顺序';
+COMMENT ON COLUMN databus_api_step.parallel_group IS '并行分组标识';
+COMMENT ON COLUMN databus_api_step.type IS '步骤类型';
+COMMENT ON COLUMN databus_api_step.target_endpoint IS '目标端点';
+COMMENT ON COLUMN databus_api_step.request_mapping_expr IS '请求映射表达式';
+COMMENT ON COLUMN databus_api_step.response_mapping_expr IS '响应映射表达式';
+COMMENT ON COLUMN databus_api_step.transform_id IS '默认变换 ID';
+COMMENT ON COLUMN databus_api_step.timeout IS '超时时间(毫秒)';
+COMMENT ON COLUMN databus_api_step.retry_strategy IS '重试策略 JSON';
+COMMENT ON COLUMN databus_api_step.fallback_strategy IS '降级策略 JSON';
+COMMENT ON COLUMN databus_api_step.condition_expr IS '执行条件表达式';
+COMMENT ON COLUMN databus_api_step.stop_on_error IS '出错是否终止';
+COMMENT ON COLUMN databus_api_step.creator IS '创建者';
+COMMENT ON COLUMN databus_api_step.create_time IS '创建时间';
+COMMENT ON COLUMN databus_api_step.updater IS '更新者';
+COMMENT ON COLUMN databus_api_step.update_time IS '更新时间';
+COMMENT ON COLUMN databus_api_step.deleted IS '逻辑删除标记';
+
+-- ----------------------------
+-- Table structure for databus_api_transform
+-- ----------------------------
+CREATE TABLE databus_api_transform (
+ id BIGINT NOT NULL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL,
+ api_id BIGINT,
+ step_id BIGINT,
+ phase VARCHAR(32) NOT NULL,
+ expression_type VARCHAR(32) NOT NULL,
+ expression CLOB NOT NULL,
+ description VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted BIT DEFAULT '0' NOT NULL
+);
+
+CREATE INDEX idx_databus_api_transform_api ON databus_api_transform (tenant_id, api_id);
+CREATE INDEX idx_databus_api_transform_step ON databus_api_transform (tenant_id, step_id);
+
+COMMENT ON TABLE databus_api_transform IS '统一外部 API 门户 - 变换配置表';
+COMMENT ON COLUMN databus_api_transform.id IS '主键 ID';
+COMMENT ON COLUMN databus_api_transform.tenant_id IS '租户编号';
+COMMENT ON COLUMN databus_api_transform.api_id IS '关联的 API ID';
+COMMENT ON COLUMN databus_api_transform.step_id IS '关联的步骤 ID';
+COMMENT ON COLUMN databus_api_transform.phase IS '执行阶段';
+COMMENT ON COLUMN databus_api_transform.expression_type IS '表达式类型';
+COMMENT ON COLUMN databus_api_transform.expression IS '表达式内容';
+COMMENT ON COLUMN databus_api_transform.description IS '描述信息';
+COMMENT ON COLUMN databus_api_transform.creator IS '创建者';
+COMMENT ON COLUMN databus_api_transform.create_time IS '创建时间';
+COMMENT ON COLUMN databus_api_transform.updater IS '更新者';
+COMMENT ON COLUMN databus_api_transform.update_time IS '更新时间';
+COMMENT ON COLUMN databus_api_transform.deleted IS '逻辑删除标记';
diff --git a/sql/dm/规则引擎核心表结构_20251014.sql b/sql/dm/规则引擎核心表结构_20251014.sql
new file mode 100644
index 00000000..0e734790
--- /dev/null
+++ b/sql/dm/规则引擎核心表结构_20251014.sql
@@ -0,0 +1,228 @@
+-- 规则引擎模块核心表结构(DM8)
+-- 如果需要重建表,请在执行前备份现有数据
+
+DROP TABLE IF EXISTS rule_release_record;
+DROP TABLE IF EXISTS rule_business_relation;
+DROP TABLE IF EXISTS rule_business;
+DROP TABLE IF EXISTS rule_chain_dependency;
+DROP TABLE IF EXISTS rule_chain;
+DROP TABLE IF EXISTS rule_definition;
+
+CREATE TABLE rule_definition (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ code VARCHAR(128) NOT NULL,
+ name VARCHAR(128) NOT NULL,
+ type SMALLINT NOT NULL,
+ dsl CLOB,
+ script_language VARCHAR(64),
+ bean_ref VARCHAR(128),
+ config_json CLOB,
+ status SMALLINT DEFAULT 0 NOT NULL,
+ version VARCHAR(64),
+ remark VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_definition IS '规则定义表';
+COMMENT ON COLUMN rule_definition.id IS '规则定义编号';
+COMMENT ON COLUMN rule_definition.code IS '规则编码';
+COMMENT ON COLUMN rule_definition.name IS '规则名称';
+COMMENT ON COLUMN rule_definition.type IS '规则类型';
+COMMENT ON COLUMN rule_definition.dsl IS 'LiteFlow DSL 脚本';
+COMMENT ON COLUMN rule_definition.script_language IS '脚本语言';
+COMMENT ON COLUMN rule_definition.bean_ref IS 'Spring Bean 引用';
+COMMENT ON COLUMN rule_definition.config_json IS '规则配置 JSON';
+COMMENT ON COLUMN rule_definition.status IS '规则状态';
+COMMENT ON COLUMN rule_definition.version IS '规则版本号';
+COMMENT ON COLUMN rule_definition.remark IS '备注';
+COMMENT ON COLUMN rule_definition.creator IS '创建者';
+COMMENT ON COLUMN rule_definition.create_time IS '创建时间';
+COMMENT ON COLUMN rule_definition.updater IS '更新者';
+COMMENT ON COLUMN rule_definition.update_time IS '更新时间';
+COMMENT ON COLUMN rule_definition.deleted IS '是否删除';
+COMMENT ON COLUMN rule_definition.tenant_id IS '租户编号';
+
+CREATE UNIQUE INDEX uk_rule_definition_code_tenant ON rule_definition (code, tenant_id);
+
+CREATE TABLE rule_chain (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ code VARCHAR(128) NOT NULL,
+ name VARCHAR(128) NOT NULL,
+ description VARCHAR(512),
+ structure_json CLOB,
+ liteflow_dsl CLOB,
+ status SMALLINT DEFAULT 0 NOT NULL,
+ version VARCHAR(64),
+ remark VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_chain IS '规则链表';
+COMMENT ON COLUMN rule_chain.id IS '规则链编号';
+COMMENT ON COLUMN rule_chain.code IS '规则链编码';
+COMMENT ON COLUMN rule_chain.name IS '规则链名称';
+COMMENT ON COLUMN rule_chain.description IS '规则链描述';
+COMMENT ON COLUMN rule_chain.structure_json IS '链路结构 JSON';
+COMMENT ON COLUMN rule_chain.liteflow_dsl IS 'LiteFlow DSL 内容';
+COMMENT ON COLUMN rule_chain.status IS '规则链状态';
+COMMENT ON COLUMN rule_chain.version IS '版本号';
+COMMENT ON COLUMN rule_chain.remark IS '备注';
+COMMENT ON COLUMN rule_chain.creator IS '创建者';
+COMMENT ON COLUMN rule_chain.create_time IS '创建时间';
+COMMENT ON COLUMN rule_chain.updater IS '更新者';
+COMMENT ON COLUMN rule_chain.update_time IS '更新时间';
+COMMENT ON COLUMN rule_chain.deleted IS '是否删除';
+COMMENT ON COLUMN rule_chain.tenant_id IS '租户编号';
+
+CREATE UNIQUE INDEX uk_rule_chain_code_tenant ON rule_chain (code, tenant_id);
+CREATE INDEX idx_rule_chain_status ON rule_chain (status);
+
+CREATE TABLE rule_chain_dependency (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ parent_chain_id BIGINT NOT NULL,
+ child_rule_id BIGINT NOT NULL,
+ link_type SMALLINT NOT NULL,
+ order_index INTEGER,
+ parallel_group VARCHAR(64),
+ condition_expr VARCHAR(512),
+ config_json CLOB,
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_chain_dependency IS '规则链依赖表';
+COMMENT ON COLUMN rule_chain_dependency.id IS '依赖编号';
+COMMENT ON COLUMN rule_chain_dependency.parent_chain_id IS '父规则链编号';
+COMMENT ON COLUMN rule_chain_dependency.child_rule_id IS '引用的规则定义编号';
+COMMENT ON COLUMN rule_chain_dependency.link_type IS '节点类型';
+COMMENT ON COLUMN rule_chain_dependency.order_index IS '执行顺序';
+COMMENT ON COLUMN rule_chain_dependency.parallel_group IS '并行组标识';
+COMMENT ON COLUMN rule_chain_dependency.condition_expr IS '条件表达式';
+COMMENT ON COLUMN rule_chain_dependency.config_json IS '节点配置 JSON';
+COMMENT ON COLUMN rule_chain_dependency.creator IS '创建者';
+COMMENT ON COLUMN rule_chain_dependency.create_time IS '创建时间';
+COMMENT ON COLUMN rule_chain_dependency.updater IS '更新者';
+COMMENT ON COLUMN rule_chain_dependency.update_time IS '更新时间';
+COMMENT ON COLUMN rule_chain_dependency.deleted IS '是否删除';
+COMMENT ON COLUMN rule_chain_dependency.tenant_id IS '租户编号';
+
+CREATE INDEX idx_rule_chain_dependency_parent ON rule_chain_dependency (parent_chain_id);
+CREATE INDEX idx_rule_chain_dependency_child ON rule_chain_dependency (child_rule_id);
+
+CREATE TABLE rule_business (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ business VARCHAR(128) NOT NULL,
+ rule_chain_id BIGINT,
+ override_strategy SMALLINT DEFAULT 0 NOT NULL,
+ locked TINYINT DEFAULT 0 NOT NULL,
+ effective_version VARCHAR(64),
+ config_json CLOB,
+ remark VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_business IS '业务规则绑定表';
+COMMENT ON COLUMN rule_business.id IS '业务绑定编号';
+COMMENT ON COLUMN rule_business.business IS '业务标识';
+COMMENT ON COLUMN rule_business.rule_chain_id IS '绑定的规则链编号';
+COMMENT ON COLUMN rule_business.override_strategy IS '覆盖策略';
+COMMENT ON COLUMN rule_business.locked IS '是否锁定';
+COMMENT ON COLUMN rule_business.effective_version IS '生效版本';
+COMMENT ON COLUMN rule_business.config_json IS '业务配置 JSON';
+COMMENT ON COLUMN rule_business.remark IS '备注';
+COMMENT ON COLUMN rule_business.creator IS '创建者';
+COMMENT ON COLUMN rule_business.create_time IS '创建时间';
+COMMENT ON COLUMN rule_business.updater IS '更新者';
+COMMENT ON COLUMN rule_business.update_time IS '更新时间';
+COMMENT ON COLUMN rule_business.deleted IS '是否删除';
+COMMENT ON COLUMN rule_business.tenant_id IS '租户编号';
+
+CREATE UNIQUE INDEX uk_rule_business_tenant ON rule_business (business, tenant_id);
+CREATE INDEX idx_rule_business_chain ON rule_business (rule_chain_id);
+
+CREATE TABLE rule_business_relation (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ parent_business VARCHAR(128) NOT NULL,
+ child_business VARCHAR(128) NOT NULL,
+ sort INTEGER DEFAULT 0 NOT NULL,
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_business_relation IS '业务继承关系表';
+COMMENT ON COLUMN rule_business_relation.id IS '继承关系编号';
+COMMENT ON COLUMN rule_business_relation.parent_business IS '父业务标识';
+COMMENT ON COLUMN rule_business_relation.child_business IS '子业务标识';
+COMMENT ON COLUMN rule_business_relation.sort IS '排序';
+COMMENT ON COLUMN rule_business_relation.creator IS '创建者';
+COMMENT ON COLUMN rule_business_relation.create_time IS '创建时间';
+COMMENT ON COLUMN rule_business_relation.updater IS '更新者';
+COMMENT ON COLUMN rule_business_relation.update_time IS '更新时间';
+COMMENT ON COLUMN rule_business_relation.deleted IS '是否删除';
+COMMENT ON COLUMN rule_business_relation.tenant_id IS '租户编号';
+
+CREATE UNIQUE INDEX uk_rule_business_relation_child ON rule_business_relation (child_business, tenant_id);
+CREATE INDEX idx_rule_business_relation_parent ON rule_business_relation (parent_business);
+
+CREATE TABLE rule_release_record (
+ id BIGINT IDENTITY(1,1) PRIMARY KEY,
+ business VARCHAR(128) NOT NULL,
+ chain_id VARCHAR(255) NOT NULL,
+ chain_code VARCHAR(128),
+ version VARCHAR(64) NOT NULL,
+ status SMALLINT DEFAULT 0 NOT NULL,
+ release_user_id BIGINT,
+ release_user_name VARCHAR(128),
+ release_time TIMESTAMP,
+ remark VARCHAR(512),
+ creator VARCHAR(64) DEFAULT '' NOT NULL,
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updater VARCHAR(64) DEFAULT '' NOT NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ tenant_id BIGINT DEFAULT 0 NOT NULL
+);
+
+COMMENT ON TABLE rule_release_record IS '规则发布记录表';
+COMMENT ON COLUMN rule_release_record.id IS '发布记录编号';
+COMMENT ON COLUMN rule_release_record.business IS '业务标识';
+COMMENT ON COLUMN rule_release_record.chain_id IS '生成链路标识';
+COMMENT ON COLUMN rule_release_record.chain_code IS '规则链编码';
+COMMENT ON COLUMN rule_release_record.version IS '发布版本';
+COMMENT ON COLUMN rule_release_record.status IS '发布状态';
+COMMENT ON COLUMN rule_release_record.release_user_id IS '发布人编号';
+COMMENT ON COLUMN rule_release_record.release_user_name IS '发布人名称';
+COMMENT ON COLUMN rule_release_record.release_time IS '发布时间';
+COMMENT ON COLUMN rule_release_record.remark IS '备注';
+COMMENT ON COLUMN rule_release_record.creator IS '创建者';
+COMMENT ON COLUMN rule_release_record.create_time IS '创建时间';
+COMMENT ON COLUMN rule_release_record.updater IS '更新者';
+COMMENT ON COLUMN rule_release_record.update_time IS '更新时间';
+COMMENT ON COLUMN rule_release_record.deleted IS '是否删除';
+COMMENT ON COLUMN rule_release_record.tenant_id IS '租户编号';
+
+CREATE INDEX idx_rule_release_business_time ON rule_release_record (business, release_time);
+CREATE UNIQUE INDEX uk_rule_release_business_version ON rule_release_record (business, version, tenant_id);
diff --git a/sql/dm/规则引擎菜单初始化_20251014.sql b/sql/dm/规则引擎菜单初始化_20251014.sql
new file mode 100644
index 00000000..095f64d2
--- /dev/null
+++ b/sql/dm/规则引擎菜单初始化_20251014.sql
@@ -0,0 +1,27 @@
+-- 规则引擎模块菜单与权限初始化(DM8)
+-- 顶级目录放置在系统管理(2)下,如需调整请修改 parent_id
+
+DELETE FROM system_menu WHERE id IN (6100,6101,610101,610102,610103,610104,610111,610112,610113,610114,610121,610122,610123,610124,610131,610132,610133,610141);
+
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+VALUES
+(6100, '规则引擎', '', 1, 20, 2, 'rule', 'ep:s-operation', '', 'RuleModule', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(6101, '规则设计器', '', 2, 1, 6100, 'designer', 'ep:s-operation', 'rule/designer/index', 'RuleDesigner', 0, '1', '0', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
+
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES
+(610101, '规则定义查询', 'rule:definition:query', 3, 1, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610102, '规则定义创建', 'rule:definition:create', 3, 2, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610103, '规则定义更新', 'rule:definition:update', 3, 3, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610104, '规则定义删除', 'rule:definition:delete', 3, 4, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610111, '规则链查询', 'rule:chain:query', 3, 5, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610112, '规则链创建', 'rule:chain:create', 3, 6, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610113, '规则链更新', 'rule:chain:update', 3, 7, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610114, '规则链删除', 'rule:chain:delete', 3, 8, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610121, '业务链查询', 'rule:business:query', 3, 9, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610122, '业务链维护', 'rule:business:update', 3, 10, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610123, '业务链删除', 'rule:business:delete', 3, 11, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610124, '业务链预览', 'rule:business:preview', 3, 12, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610131, '发布记录查询', 'rule:publish:query', 3, 13, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610132, '规则链发布', 'rule:publish:publish', 3, 14, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610133, '规则链回滚', 'rule:publish:rollback', 3, 15, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
+(610141, '规则模拟执行', 'rule:simulation:execute', 3, 16, 6101, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
diff --git a/sql/dm/角色权限剔除表.sql b/sql/dm/角色权限剔除表_20250623.sql
similarity index 100%
rename from sql/dm/角色权限剔除表.sql
rename to sql/dm/角色权限剔除表_20250623.sql
diff --git a/sql/dm/部门外部组织编码映射初始化_DM8.sql b/sql/dm/部门外部组织编码映射初始化_DM8.sql
new file mode 100644
index 00000000..a1a2ae86
--- /dev/null
+++ b/sql/dm/部门外部组织编码映射初始化_DM8.sql
@@ -0,0 +1,60 @@
+-- DM8 部门外部组织编码映射初始化脚本
+-- 包含表结构、字段注释及基础字典数据
+
+-- 重复执行时请先备份数据
+DROP TABLE IF EXISTS system_dept_external_code;
+
+CREATE TABLE system_dept_external_code (
+ id BIGINT NOT NULL,
+ dept_id BIGINT NOT NULL,
+ system_code VARCHAR(64) NOT NULL,
+ external_dept_code VARCHAR(128) NOT NULL,
+ external_dept_name VARCHAR(255),
+ status TINYINT DEFAULT 0 NOT NULL,
+ remark VARCHAR(512),
+ tenant_id BIGINT DEFAULT 0,
+ creator VARCHAR(64),
+ create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updater VARCHAR(64),
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted TINYINT DEFAULT 0 NOT NULL,
+ CONSTRAINT pk_system_dept_external_code PRIMARY KEY (id)
+);
+
+-- 唯一索引与辅助索引
+CREATE UNIQUE INDEX uk_system_dept_external_code_ext
+ ON system_dept_external_code (tenant_id, system_code, external_dept_code);
+CREATE UNIQUE INDEX uk_system_dept_external_code_dept
+ ON system_dept_external_code (tenant_id, system_code, dept_id);
+CREATE INDEX idx_system_dept_external_code_dept
+ ON system_dept_external_code (tenant_id, dept_id);
+
+COMMENT ON TABLE system_dept_external_code IS '部门外部组织编码映射';
+COMMENT ON COLUMN system_dept_external_code.id IS '主键编号';
+COMMENT ON COLUMN system_dept_external_code.dept_id IS '本系统部门编号';
+COMMENT ON COLUMN system_dept_external_code.system_code IS '外部系统标识';
+COMMENT ON COLUMN system_dept_external_code.external_dept_code IS '外部组织编码';
+COMMENT ON COLUMN system_dept_external_code.external_dept_name IS '外部组织名称';
+COMMENT ON COLUMN system_dept_external_code.status IS '状态(0开启 1关闭)';
+COMMENT ON COLUMN system_dept_external_code.remark IS '备注';
+COMMENT ON COLUMN system_dept_external_code.tenant_id IS '租户编号';
+COMMENT ON COLUMN system_dept_external_code.creator IS '创建者';
+COMMENT ON COLUMN system_dept_external_code.create_time IS '创建时间';
+COMMENT ON COLUMN system_dept_external_code.updater IS '更新者';
+COMMENT ON COLUMN system_dept_external_code.update_time IS '更新时间';
+COMMENT ON COLUMN system_dept_external_code.deleted IS '删除标记';
+
+-- 初始化外部系统标识字典
+INSERT INTO system_dict_type (id, name, type, status, remark, creator, create_time, updater, update_time, deleted)
+SELECT 20050, '外部系统标识', 'system_dept_external_system', 0, '部门外部组织编码中的外部系统标识', 'admin', SYSDATE, 'admin', SYSDATE, 0
+FROM dual
+WHERE NOT EXISTS (
+ SELECT 1 FROM system_dict_type WHERE type = 'system_dept_external_system'
+);
+
+INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted)
+SELECT 2005001, 1, 'ERP 系统', 'ERP', 'system_dept_external_system', 0, '', '', '企业资源计划系统', 'admin', SYSDATE, 'admin', SYSDATE, 0
+FROM dual
+WHERE NOT EXISTS (
+ SELECT 1 FROM system_dict_data WHERE dict_type = 'system_dept_external_system' AND value = 'ERP'
+);
diff --git a/sql/dm/部门外部组织编码映射菜单权限_DM8.sql b/sql/dm/部门外部组织编码映射菜单权限_DM8.sql
new file mode 100644
index 00000000..4c726aff
--- /dev/null
+++ b/sql/dm/部门外部组织编码映射菜单权限_DM8.sql
@@ -0,0 +1,35 @@
+-- DM8 部门外部组织编码映射菜单与权限脚本
+-- 清理旧数据并重新创建目录、页面及操作按钮
+
+-- 保持脚本幂等性,先清理旧数据
+DELETE FROM system_role_menu WHERE menu_id IN (6200, 6201, 620101, 620102, 620103, 620104);
+DELETE FROM system_menu WHERE id IN (6200, 6201, 620101, 620102, 620103, 620104);
+
+-- 在系统管理(ID=2)下创建目录与页面
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+) VALUES (
+ 6200, '组织编码映射', '', 1, 25, 2,
+ 'dept-external', 'ep:connection', '', 0, 'DeptExternalCodeRoot'
+);
+
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+) VALUES (
+ 6201, '外部组织编码', '', 2, 1, 6200,
+ 'dept-external-code', 'ep:connection', 'system/deptExternalCode/index', 0, 'SystemDeptExternalCode'
+);
+
+-- 创建操作按钮权限
+INSERT INTO system_menu (
+ id, name, permission, type, sort, parent_id,
+ path, icon, component, status
+) VALUES
+ (620101, '查询部门外部编码', 'system:dept-external-code:query', 3, 1, 6201, '', '', '', 0),
+ (620102, '新增部门外部编码', 'system:dept-external-code:create', 3, 2, 6201, '', '', '', 0),
+ (620103, '修改部门外部编码', 'system:dept-external-code:update', 3, 3, 6201, '', '', '', 0),
+ (620104, '删除部门外部编码', 'system:dept-external-code:delete', 3, 4, 6201, '', '', '', 0);
+
+-- 如需分配给角色,请按本地序列策略写入 system_role_menu
diff --git a/sql/dm/部门添加编码、简称字段.sql b/sql/dm/部门添加编码、简称字段_20250918.sql
similarity index 100%
rename from sql/dm/部门添加编码、简称字段.sql
rename to sql/dm/部门添加编码、简称字段_20250918.sql
diff --git a/sql/mysql/databus_sample_data.sql b/sql/mysql/databus_sample_data.sql
new file mode 100644
index 00000000..260b66ee
--- /dev/null
+++ b/sql/mysql/databus_sample_data.sql
@@ -0,0 +1,65 @@
+START TRANSACTION;
+
+-- Cleanup previous sample records by identifier range
+DELETE FROM databus_api_transform WHERE id BETWEEN 520100000000000000 AND 520100000000000999;
+DELETE FROM databus_api_step WHERE id BETWEEN 610100000000000000 AND 610100000000000999;
+DELETE FROM databus_api_definition WHERE id BETWEEN 410100000000000000 AND 410100000000000999;
+DELETE FROM databus_policy_auth WHERE id BETWEEN 110100000000000000 AND 110100000000000999;
+DELETE FROM databus_policy_rate_limit WHERE id BETWEEN 210100000000000000 AND 210100000000000999;
+DELETE FROM databus_api_flow_publish WHERE id BETWEEN 710100000000000000 AND 710100000000000999;
+
+-- Authentication policies aligned with DefaultAuthPolicyEvaluator header strategy
+INSERT INTO databus_policy_auth
+ (id, tenant_id, name, type, config, description, creator, create_time, updater, update_time, deleted)
+VALUES
+ (110100000000000001, 1, '统一 Token 校验', 'HEADER_TOKEN', '{"allowedTokens":[]}', '通过 ZT-Auth-Token 传递访问凭证', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (110100000000000002, 1, '内部服务白名单', 'INTERNAL_TRUSTED', '{"allowedTokens":["system-server","databus-server"]}', '内部系统间调用的白名单策略', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (110100000000000003, 1, '调试临时凭证', 'HEADER_TOKEN', '{"allowedTokens":["debug-token"]}', '用于灰度测试的临时凭证', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0);
+
+-- Rate limit policies compatible with DefaultRateLimitPolicyEvaluator
+INSERT INTO databus_policy_rate_limit
+ (id, tenant_id, name, type, config, description, creator, create_time, updater, update_time, deleted)
+VALUES
+ (210100000000000001, 1, '公共查询 120 RPM', 'FIXED_WINDOW', '{"limit":120,"windowSeconds":60}', '公共查询接口的分钟级限流', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (210100000000000002, 1, '用户画像 30 RPM', 'FIXED_WINDOW', '{"limit":30,"windowSeconds":60}', '用户画像聚合接口的限流策略', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (210100000000000003, 1, '登录试用 60 RPM', 'FIXED_WINDOW', '{"limit":60,"windowSeconds":60}', '测试登录能力的限流', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0);
+
+-- API definitions referencing real system modules
+INSERT INTO databus_api_definition
+ (id, tenant_id, api_code, uri_pattern, http_method, version, status, description, auth_policy_id, rate_limit_id, response_template, cache_strategy, updated_at, grey_released, creator, create_time, updater, update_time, deleted)
+VALUES
+ (410100000000000001, 1, 'system.lookup.bundle', '/external/system/lookup-bundle', 'GET', 'v1', 1, '聚合系统用户、部门、字典精简列表的只读接口', 110100000000000001, 210100000000000001, '{"code":0,"message":"success","data":{}}', '{"provider":"redis","ttlSeconds":120,"cacheKey":"system:lookup:bundle"}', CURRENT_TIMESTAMP, 0, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (410100000000000002, 1, 'system.user.profile.aggregate', '/external/system/user/profile', 'POST', 'v1', 1, '根据 userId 聚合后台用户、角色及部门信息', 110100000000000001, 210100000000000002, '{"code":0,"message":"success","data":{}}', NULL, CURRENT_TIMESTAMP, 0, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (410100000000000003, 1, 'system.auth.quick-login', '/external/system/auth/quick-login', 'POST', 'v1', 1, '调用测试登录接口并返回用户基础画像', 110100000000000001, 210100000000000003, '{"code":0,"message":"success","data":{}}', NULL, CURRENT_TIMESTAMP, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0);
+
+-- API level transforms
+INSERT INTO databus_api_transform
+ (id, tenant_id, api_id, step_id, phase, expression_type, expression, description, creator, create_time, updater, update_time, deleted)
+VALUES
+ (520100000000000101, 1, 410100000000000001, NULL, 'REQUEST_PRE', 'JSON', '($trace := $ctx.requestHeaders."X-Trace-Id"; {"requestHeaders": {"X-Trace-Id": $trace ? $trace : $uuid()}})', '自动补全链路追踪 ID', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (520100000000000102, 1, 410100000000000001, NULL, 'RESPONSE_PRE', 'JSON', '{"responseBody": {"users": $vars.users ? $vars.users : [], "departments": $vars.departments ? $vars.departments : [], "dicts": $vars.dicts ? $vars.dicts : []}}', '组装统一响应结构', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (520100000000000103, 1, 410100000000000002, NULL, 'RESPONSE_PRE', 'JSON', '{"responseBody": {"user": $vars.user, "roleIds": $vars.roleIds ? $vars.roleIds : [], "roles": $vars.roles ? $vars.roles : [], "departments": $vars.departments ? $vars.departments : []}}', '聚合用户详情返回体', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (520100000000000104, 1, 410100000000000003, NULL, 'RESPONSE_PRE', 'JSON', '{"responseBody": {"tokens": $vars.tokens, "loginUser": $vars.loginUser, "companyDept": $vars.companyDept ? $vars.companyDept : []}}', '组合测试登录返回体', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0);
+
+-- API orchestration steps referencing real HTTP endpoints and Spring beans
+INSERT INTO databus_api_step
+ (id, tenant_id, api_id, step_order, parallel_group, type, target_endpoint, request_mapping_expr, response_mapping_expr, transform_id, timeout, retry_strategy, fallback_strategy, condition_expr, stop_on_error, creator, create_time, updater, update_time, deleted)
+VALUES
+ (610100000000000201, 1, 410100000000000001, 1, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/user/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/user/list-all-simple 失败: " & $.msg) : {"users": $.data ? $.data : []})', NULL, 5000, '{"maxAttempts":2,"delayMs":200}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000202, 1, 410100000000000001, 2, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/dept/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/dept/list-all-simple 失败: " & $.msg) : {"departments": $.data ? $.data : []})', NULL, 5000, '{"maxAttempts":2,"delayMs":200}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000203, 1, 410100000000000001, 3, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/dict-data/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/dict-data/list-all-simple 失败: " & $.msg) : {"dicts": $.data ? $.data : []})', NULL, 5000, '{"maxAttempts":2,"delayMs":200}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000204, 1, 410100000000000002, 1, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/user/get', 'JSON::{"id": $.userId}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/user/get 失败: " & $.msg) : {"user": $.data})', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000205, 1, 410100000000000002, 2, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/permission/list-user-roles', 'JSON::($user := $vars.user; {"userId": $user ? $user.id : null})', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/permission/list-user-roles 失败: " & $.msg) : ($data := $.data ? $.data : []; {"roleIds": $data.($string($))}))', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000206, 1, 410100000000000002, 3, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/role/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/role/list-all-simple 失败: " & $.msg) : ($data := $.data ? $.data : []; {"roles": $data[$contains($vars.roleIds, $string(id))]}))', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000207, 1, 410100000000000002, 4, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/dept/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/dept/list-all-simple 失败: " & $.msg) : ($data := $.data ? $.data : []; $deptIds := $vars.user.deptIds ? $vars.user.deptIds.($string($)) : []; {"departments": $deptIds ? $data[$contains($deptIds, $string(id))] : []}))', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000208, 1, 410100000000000003, 1, NULL, 'HTTP', 'POST http://127.0.0.1:48080/admin-api/system/auth/test-login', 'JSON::{"username": $.username, "password": $.password}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/auth/test-login 失败: " & $.msg) : {"tokens": $.data})', NULL, 5000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000209, 1, 410100000000000003, 2, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/user/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/user/list-all-simple 失败: " & $.msg) : ($data := $.data ? $.data : []; {"loginUser": $data[$string(id) = $string($vars.tokens.userId)][0]}))', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+ (610100000000000210, 1, 410100000000000003, 3, NULL, 'HTTP', 'GET http://127.0.0.1:48080/admin-api/system/dept/list-all-simple', 'JSON::{}', 'JSON::($.code != 0 ? $error("调用 /admin-api/system/dept/list-all-simple 失败: " & $.msg) : ($data := $.data ? $.data : []; {"companyDept": $vars.loginUser and $vars.loginUser.deptId ? $data[$string(id) = $string($vars.loginUser.deptId)] : []}))', NULL, 3000, '{"maxAttempts":1}', NULL, NULL, 1, 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0),
+
+-- Optional: publish record to illustrate version management
+INSERT INTO databus_api_flow_publish
+ (id, tenant_id, api_id, release_tag, snapshot, status, active, description, creator, create_time, updater, update_time, deleted)
+VALUES
+ (710100000000000001, 1, 410100000000000001, '2025.10.01-INIT', '{"definitionId":410100000000000001,"version":"v1","steps":3}', 'SUCCESS', 1, '初始发布 system.lookup.bundle 接口', 'sample_loader', CURRENT_TIMESTAMP, 'sample_loader', CURRENT_TIMESTAMP, 0);
+
+COMMIT;
diff --git a/sql/tools/convertor.py b/sql/tools/convertor.py
index 1ea0f629..5b5be6dd 100644
--- a/sql/tools/convertor.py
+++ b/sql/tools/convertor.py
@@ -837,7 +837,7 @@ def main():
)
args = parser.parse_args()
- sql_file = pathlib.Path("../mysql/在线文档管理表结构.sql").resolve().as_posix()
+ sql_file = pathlib.Path("../mysql/在线文档管理表结构_20250901.sql").resolve().as_posix()
convertor = None
if args.type == "postgres":
convertor = PostgreSQLConvertor(sql_file)
diff --git a/zt-gateway/src/main/resources/application.yaml b/zt-gateway/src/main/resources/application.yaml
index 8414b41c..2d837261 100644
--- a/zt-gateway/src/main/resources/application.yaml
+++ b/zt-gateway/src/main/resources/application.yaml
@@ -186,6 +186,13 @@ spring:
- Path=/admin-api/crm/**
filters:
- RewritePath=/admin-api/crm/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
+ ## rule-server 服务
+ - id: rule-admin-api # 路由的编号
+ uri: grayLb://rule-server
+ predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
+ - Path=/admin-api/rule/**
+ filters:
+ - RewritePath=/admin-api/rule/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
## ai-server 服务
- id: ai-admin-api # 路由的编号
uri: grayLb://ai-server
@@ -207,6 +214,13 @@ spring:
- Path=/admin-api/template/**
filters:
- RewritePath=/admin-api/template/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
+ ## databus-server 服务
+ - id: databus-admin-api # 路由的编号
+ uri: grayLb://databus-server
+ predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
+ - Path=/admin-api/databus/**
+ filters:
+ - RewritePath=/admin-api/databus/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
x-forwarded:
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
@@ -258,6 +272,9 @@ knife4j:
- name: crm-server
service-name: crm-server
url: /admin-api/crm/v3/api-docs
+ - name: rule-server
+ service-name: rule-server
+ url: /admin-api/rule/v3/api-docs
- name: ai-server
service-name: ai-server
url: /admin-api/ai/v3/api-docs
@@ -267,6 +284,9 @@ knife4j:
- name: template-server
service-name: template-server
url: /admin-api/template/v3/api-docs
+ - name: databus-server
+ service-name: databus-server
+ url: /admin-api/databus/v3/api-docs
--- #################### 芋道相关配置 ####################
diff --git a/zt-module-databus/zt-module-databus-server/pom.xml b/zt-module-databus/zt-module-databus-server/pom.xml
index 79a93f1f..0d51180c 100644
--- a/zt-module-databus/zt-module-databus-server/pom.xml
+++ b/zt-module-databus/zt-module-databus-server/pom.xml
@@ -126,6 +126,63 @@
zt-spring-boot-starter-biz-business
${revision}
+
+
+
+ org.springframework.integration
+ spring-integration-http
+
+
+ org.springframework.integration
+ spring-integration-core
+
+
+ org.springframework.integration
+ spring-integration-scripting
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+
+ com.ibm.jsonata4java
+ JSONata4Java
+ 2.5.5
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+ org.mvel
+ mvel2
+ 2.5.2.Final
+
+
+ commons-codec
+ commons-codec
+
+
+
+
+ org.springframework.integration
+ spring-integration-test
+ test
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ 4.12.0
+ test
+
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/databus/DatabusController.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/databus/DatabusController.java
deleted file mode 100644
index 742245d0..00000000
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/databus/DatabusController.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.zt.plat.module.databus.controller.admin.databus;
-
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.zt.plat.framework.common.pojo.CommonResult;
-
-import static com.zt.plat.framework.common.pojo.CommonResult.success;
-
-/**
- * Databus 控制器
- *
- * @author ZT
- */
-@Tag(name = "管理后台 - Databus")
-@RestController
-@RequestMapping("/admin/databus/databus")
-public class DatabusController {
-
- @GetMapping("/hello")
- @Operation(summary = "Hello Databus")
- public CommonResult hello() {
- return success("Hello, Databus!");
- }
-
-}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiDefinitionController.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiDefinitionController.java
new file mode 100644
index 00000000..b8c0d28c
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiDefinitionController.java
@@ -0,0 +1,79 @@
+package com.zt.plat.module.databus.controller.admin.gateway;
+
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiDefinitionConvert;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionDetailRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionPageReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionSaveReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionSummaryRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiDefinitionDO;
+import com.zt.plat.module.databus.framework.integration.gateway.core.IntegrationFlowManager;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
+import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static com.zt.plat.framework.common.pojo.CommonResult.success;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_DEFINITION_NOT_FOUND;
+
+@Tag(name = "管理后台 - API 定义管理")
+@RestController
+@RequestMapping("/databus/gateway/definition")
+@RequiredArgsConstructor
+@Validated
+public class ApiDefinitionController {
+
+ private final ApiDefinitionService apiDefinitionService;
+ private final IntegrationFlowManager integrationFlowManager;
+
+ @GetMapping("/page")
+ @Operation(summary = "分页查询 API 定义")
+ public CommonResult> getDefinitionPage(@Valid ApiDefinitionPageReqVO reqVO) {
+ PageResult pageResult = apiDefinitionService.getPage(reqVO);
+ return success(ApiDefinitionConvert.INSTANCE.convertPage(pageResult));
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "获取 API 定义详情")
+ public CommonResult getDefinition(@PathVariable("id") Long id) {
+ ApiDefinitionAggregate aggregate = apiDefinitionService.findById(id)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_DEFINITION_NOT_FOUND));
+ return success(ApiDefinitionConvert.INSTANCE.convert(aggregate));
+ }
+
+ @PostMapping
+ @Operation(summary = "创建 API 定义")
+ public CommonResult createDefinition(@Valid @RequestBody ApiDefinitionSaveReqVO reqVO) {
+ Long id = apiDefinitionService.create(reqVO);
+ integrationFlowManager.refresh(reqVO.getApiCode(), reqVO.getVersion());
+ return success(id);
+ }
+
+ @PutMapping
+ @Operation(summary = "更新 API 定义")
+ public CommonResult updateDefinition(@Valid @RequestBody ApiDefinitionSaveReqVO reqVO) {
+ ApiDefinitionAggregate before = apiDefinitionService.findById(reqVO.getId())
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_DEFINITION_NOT_FOUND));
+ apiDefinitionService.update(reqVO);
+ integrationFlowManager.refresh(before.getDefinition().getApiCode(), before.getDefinition().getVersion());
+ integrationFlowManager.refresh(reqVO.getApiCode(), reqVO.getVersion());
+ return success(Boolean.TRUE);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除 API 定义")
+ public CommonResult deleteDefinition(@PathVariable("id") Long id) {
+ ApiDefinitionAggregate aggregate = apiDefinitionService.findById(id)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_DEFINITION_NOT_FOUND));
+ apiDefinitionService.delete(id);
+ integrationFlowManager.refresh(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
+ return success(Boolean.TRUE);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiGatewayController.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiGatewayController.java
new file mode 100644
index 00000000..e7a44307
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiGatewayController.java
@@ -0,0 +1,107 @@
+package com.zt.plat.module.databus.controller.admin.gateway;
+
+import com.zt.plat.framework.common.exception.ServiceException;
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiDefinitionConvert;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionDetailRespVO;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ApiFlowDispatcher;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.zt.plat.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - API 门户")
+@RestController
+@RequestMapping("/databus/gateway")
+@RequiredArgsConstructor
+public class ApiGatewayController {
+
+ private final ApiFlowDispatcher apiFlowDispatcher;
+ private final ApiDefinitionService apiDefinitionService;
+
+ @PostMapping(value = "/invoke", consumes = MediaType.APPLICATION_JSON_VALUE)
+ @Operation(summary = "测试调用 API 编排")
+ public CommonResult invoke(@RequestBody ApiGatewayInvokeReqVO reqVO) {
+ ApiInvocationContext context = ApiInvocationContext.create();
+ context.setApiCode(reqVO.getApiCode());
+ context.setApiVersion(reqVO.getVersion());
+ context.setRequestBody(reqVO.getPayload());
+ if (reqVO.getHeaders() != null) {
+ context.getRequestHeaders().putAll(reqVO.getHeaders());
+ }
+ if (reqVO.getQueryParams() != null) {
+ context.getRequestQueryParams().putAll(reqVO.getQueryParams());
+ }
+
+ ApiInvocationContext responseContext = context;
+ try {
+ responseContext = apiFlowDispatcher.dispatch(reqVO.getApiCode(), reqVO.getVersion(), context);
+ } catch (ServiceException ex) {
+ handleServiceException(responseContext, ex);
+ } catch (Exception ex) {
+ handleUnexpectedException(responseContext, ex);
+ }
+
+ int status = responseContext.getResponseStatus() != null ? responseContext.getResponseStatus() : HttpStatus.OK.value();
+ String message = StringUtils.hasText(responseContext.getResponseMessage())
+ ? responseContext.getResponseMessage()
+ : HttpStatus.valueOf(status).getReasonPhrase();
+
+ ApiGatewayResponse envelope = ApiGatewayResponse.builder()
+ .code(status >= 200 && status < 400 ? "SUCCESS" : "ERROR")
+ .message(message)
+ .data(responseContext.getResponseBody())
+ .traceId(responseContext.getRequestId())
+ .build();
+ return success(envelope);
+ }
+
+ @GetMapping("/definitions")
+ @Operation(summary = "获取当前已发布 API 配置")
+ public CommonResult> listDefinitions() {
+ List definitions = apiDefinitionService.loadActiveDefinitions().stream()
+ .map(ApiDefinitionConvert.INSTANCE::convert)
+ .collect(Collectors.toList());
+ return success(definitions);
+ }
+
+ private void handleServiceException(ApiInvocationContext context, ServiceException ex) {
+ String message = StringUtils.hasText(ex.getMessage()) ? ex.getMessage() : "API 调用失败";
+ context.setResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
+ context.setResponseMessage(message);
+ Map body = new HashMap<>();
+ if (ex.getCode() != null) {
+ body.put("errorCode", ex.getCode());
+ }
+ body.put("errorMessage", message);
+ context.setResponseBody(body);
+ }
+
+ private void handleUnexpectedException(ApiInvocationContext context, Exception ex) {
+ String message = StringUtils.hasText(ex.getMessage())
+ ? ex.getMessage()
+ : ex.getCause() != null && StringUtils.hasText(ex.getCause().getMessage())
+ ? ex.getCause().getMessage()
+ : "API invocation encountered an unexpected error";
+ context.setResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
+ context.setResponseMessage(message);
+ Map body = new HashMap<>();
+ body.put("errorMessage", message);
+ body.put("exception", ex.getClass().getSimpleName());
+ context.setResponseBody(body);
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyAuthController.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyAuthController.java
new file mode 100644
index 00000000..9547c46b
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyAuthController.java
@@ -0,0 +1,84 @@
+package com.zt.plat.module.databus.controller.admin.gateway;
+
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiPolicyAuthConvert;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyPageReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySaveReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySimpleRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyAuthDO;
+import com.zt.plat.module.databus.service.gateway.ApiPolicyAuthService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static com.zt.plat.framework.common.pojo.CommonResult.success;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_POLICY_NOT_FOUND;
+
+@Tag(name = "管理后台 - 网关认证策略")
+@RestController
+@RequestMapping("/databus/gateway/policy/auth")
+@RequiredArgsConstructor
+@Validated
+public class ApiPolicyAuthController {
+
+ private final ApiPolicyAuthService authService;
+
+ @GetMapping("/page")
+ @Operation(summary = "分页查询认证策略")
+ public CommonResult> getAuthPolicyPage(@Valid ApiPolicyPageReqVO reqVO) {
+ PageResult pageResult = authService.getPage(reqVO);
+ return success(ApiPolicyAuthConvert.INSTANCE.convertPage(pageResult));
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "查询认证策略详情")
+ public CommonResult getAuthPolicy(@PathVariable("id") Long id) {
+ ApiPolicyAuthDO policy = authService.get(id)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_POLICY_NOT_FOUND));
+ return success(ApiPolicyAuthConvert.INSTANCE.convert(policy));
+ }
+
+ @GetMapping("/simple-list")
+ @Operation(summary = "获取认证策略精简列表")
+ public CommonResult> getAuthPolicySimpleList() {
+ List list = authService.getSimpleList();
+ return success(ApiPolicyAuthConvert.INSTANCE.convertSimpleList(list));
+ }
+
+ @PostMapping
+ @Operation(summary = "创建认证策略")
+ public CommonResult createAuthPolicy(@Valid @RequestBody ApiPolicySaveReqVO reqVO) {
+ Long id = authService.create(reqVO);
+ return success(id);
+ }
+
+ @PutMapping
+ @Operation(summary = "更新认证策略")
+ public CommonResult updateAuthPolicy(@Valid @RequestBody ApiPolicySaveReqVO reqVO) {
+ authService.update(reqVO);
+ return success(Boolean.TRUE);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除认证策略")
+ public CommonResult deleteAuthPolicy(@PathVariable("id") Long id) {
+ authService.delete(id);
+ return success(Boolean.TRUE);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyRateLimitController.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyRateLimitController.java
new file mode 100644
index 00000000..6c6e06a1
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/ApiPolicyRateLimitController.java
@@ -0,0 +1,84 @@
+package com.zt.plat.module.databus.controller.admin.gateway;
+
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiPolicyRateLimitConvert;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyPageReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySaveReqVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySimpleRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyRateLimitDO;
+import com.zt.plat.module.databus.service.gateway.ApiPolicyRateLimitService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static com.zt.plat.framework.common.pojo.CommonResult.success;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_POLICY_NOT_FOUND;
+
+@Tag(name = "管理后台 - 网关限流策略")
+@RestController
+@RequestMapping("/databus/gateway/policy/rate-limit")
+@RequiredArgsConstructor
+@Validated
+public class ApiPolicyRateLimitController {
+
+ private final ApiPolicyRateLimitService rateLimitService;
+
+ @GetMapping("/page")
+ @Operation(summary = "分页查询限流策略")
+ public CommonResult> getRateLimitPolicyPage(@Valid ApiPolicyPageReqVO reqVO) {
+ PageResult pageResult = rateLimitService.getPage(reqVO);
+ return success(ApiPolicyRateLimitConvert.INSTANCE.convertPage(pageResult));
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "查询限流策略详情")
+ public CommonResult getRateLimitPolicy(@PathVariable("id") Long id) {
+ ApiPolicyRateLimitDO policy = rateLimitService.get(id)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_POLICY_NOT_FOUND));
+ return success(ApiPolicyRateLimitConvert.INSTANCE.convert(policy));
+ }
+
+ @GetMapping("/simple-list")
+ @Operation(summary = "获取限流策略精简列表")
+ public CommonResult> getRateLimitPolicySimpleList() {
+ List list = rateLimitService.getSimpleList();
+ return success(ApiPolicyRateLimitConvert.INSTANCE.convertSimpleList(list));
+ }
+
+ @PostMapping
+ @Operation(summary = "创建限流策略")
+ public CommonResult createRateLimitPolicy(@Valid @RequestBody ApiPolicySaveReqVO reqVO) {
+ Long id = rateLimitService.create(reqVO);
+ return success(id);
+ }
+
+ @PutMapping
+ @Operation(summary = "更新限流策略")
+ public CommonResult updateRateLimitPolicy(@Valid @RequestBody ApiPolicySaveReqVO reqVO) {
+ rateLimitService.update(reqVO);
+ return success(Boolean.TRUE);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除限流策略")
+ public CommonResult deleteRateLimitPolicy(@PathVariable("id") Long id) {
+ rateLimitService.delete(id);
+ return success(Boolean.TRUE);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiDefinitionConvert.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiDefinitionConvert.java
new file mode 100644
index 00000000..8725b204
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiDefinitionConvert.java
@@ -0,0 +1,104 @@
+package com.zt.plat.module.databus.controller.admin.gateway.convert;
+
+import cn.hutool.core.collection.CollUtil;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.framework.common.util.object.BeanUtils;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionDetailRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionPublicationRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionStepRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionSummaryRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionTransformRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiDefinitionDO;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiFlowPublication;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiStepDefinition;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiTransformDefinition;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Mapper
+public interface ApiDefinitionConvert {
+
+ ApiDefinitionConvert INSTANCE = Mappers.getMapper(ApiDefinitionConvert.class);
+
+ ApiDefinitionSummaryRespVO convert(ApiDefinitionDO bean);
+
+ List convertList(List list);
+
+ default PageResult convertPage(PageResult page) {
+ if (page == null) {
+ return PageResult.empty();
+ }
+ PageResult result = new PageResult<>();
+ List list = convertList(page.getList());
+ result.setList(list == null ? new ArrayList<>() : list);
+ result.setTotal(page.getTotal());
+ return result;
+ }
+
+ default ApiDefinitionDetailRespVO convert(ApiDefinitionAggregate aggregate) {
+ if (aggregate == null) {
+ return null;
+ }
+ ApiDefinitionDetailRespVO detail = BeanUtils.toBean(aggregate.getDefinition(), ApiDefinitionDetailRespVO.class);
+ detail.setApiLevelTransforms(convertTransforms(aggregate.getDefinition().getId(), aggregate.getApiLevelTransforms().values()));
+ detail.setSteps(convertSteps(aggregate.getSteps()));
+ detail.setPublication(convert(aggregate.getPublication()));
+ return detail;
+ }
+
+ default List convertSteps(List steps) {
+ if (CollUtil.isEmpty(steps)) {
+ return new ArrayList<>();
+ }
+ return steps.stream()
+ .sorted(Comparator.comparing(step -> step.getStep().getStepOrder() == null ? Integer.MAX_VALUE : step.getStep().getStepOrder()))
+ .map(step -> {
+ ApiDefinitionStepRespVO resp = BeanUtils.toBean(step.getStep(), ApiDefinitionStepRespVO.class);
+ resp.setTransforms(convertStepTransforms(step.getStep().getApiId(), step.getStep().getId(), step.getTransforms()));
+ return resp;
+ })
+ .collect(Collectors.toList());
+ }
+
+ default List convertTransforms(Long apiId, Collection transforms) {
+ if (CollUtil.isEmpty(transforms)) {
+ return new ArrayList<>();
+ }
+ return transforms.stream()
+ .sorted(Comparator.comparing(ApiTransformDefinition::getPhase, Comparator.nullsLast(String::compareTo)))
+ .map(transform -> {
+ ApiDefinitionTransformRespVO resp = BeanUtils.toBean(transform, ApiDefinitionTransformRespVO.class);
+ resp.setApiId(apiId);
+ resp.setStepId(null);
+ return resp;
+ })
+ .collect(Collectors.toList());
+ }
+
+ default List convertStepTransforms(Long apiId, Long stepId, List transforms) {
+ if (CollUtil.isEmpty(transforms)) {
+ return new ArrayList<>();
+ }
+ return transforms.stream()
+ .sorted(Comparator.comparing(ApiTransformDefinition::getPhase, Comparator.nullsLast(String::compareTo)))
+ .map(transform -> {
+ ApiDefinitionTransformRespVO resp = BeanUtils.toBean(transform, ApiDefinitionTransformRespVO.class);
+ resp.setApiId(apiId);
+ resp.setStepId(stepId);
+ return resp;
+ })
+ .collect(Collectors.toList());
+ }
+
+ default ApiDefinitionPublicationRespVO convert(ApiFlowPublication publication) {
+ return publication == null ? null : BeanUtils.toBean(publication, ApiDefinitionPublicationRespVO.class);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyAuthConvert.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyAuthConvert.java
new file mode 100644
index 00000000..3220d221
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyAuthConvert.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.controller.admin.gateway.convert;
+
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySimpleRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyAuthDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ApiPolicyAuthConvert {
+
+ ApiPolicyAuthConvert INSTANCE = Mappers.getMapper(ApiPolicyAuthConvert.class);
+
+ ApiPolicyRespVO convert(ApiPolicyAuthDO bean);
+
+ List convertList(List list);
+
+ PageResult convertPage(PageResult page);
+
+ List convertSimpleList(List list);
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyRateLimitConvert.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyRateLimitConvert.java
new file mode 100644
index 00000000..8149b1c5
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/convert/ApiPolicyRateLimitConvert.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.controller.admin.gateway.convert;
+
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyRespVO;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicySimpleRespVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyRateLimitDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ApiPolicyRateLimitConvert {
+
+ ApiPolicyRateLimitConvert INSTANCE = Mappers.getMapper(ApiPolicyRateLimitConvert.class);
+
+ ApiPolicyRespVO convert(ApiPolicyRateLimitDO bean);
+
+ List convertList(List list);
+
+ PageResult convertPage(PageResult page);
+
+ List convertSimpleList(List list);
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/ApiGatewayInvokeReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/ApiGatewayInvokeReqVO.java
new file mode 100644
index 00000000..1b141db3
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/ApiGatewayInvokeReqVO.java
@@ -0,0 +1,27 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class ApiGatewayInvokeReqVO {
+
+ @Schema(description = "API 编码", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String apiCode;
+
+ @Schema(description = "API 版本", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String version;
+
+ @Schema(description = "请求头,可选")
+ private Map headers = new HashMap<>();
+
+ @Schema(description = "请求参数,可选")
+ private Map queryParams = new HashMap<>();
+
+ @Schema(description = "请求体")
+ private Object payload;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionDetailRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionDetailRespVO.java
new file mode 100644
index 00000000..dda4cf3b
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionDetailRespVO.java
@@ -0,0 +1,74 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@Schema(description = "管理后台 - API 定义详情 Response VO")
+public class ApiDefinitionDetailRespVO {
+
+ @Schema(description = "主键", example = "1024")
+ private Long id;
+
+ @Schema(description = "租户标识", example = "1")
+ private String tenantId;
+
+ @Schema(description = "API 编码", example = "order.create")
+ private String apiCode;
+
+ @Schema(description = "API 版本", example = "v1")
+ private String version;
+
+ @Schema(description = "HTTP 方法", example = "POST")
+ private String httpMethod;
+
+ @Schema(description = "URI 模板", example = "/external/order/create")
+ private String uriPattern;
+
+ @Schema(description = "状态", example = "1")
+ private Integer status;
+
+ @Schema(description = "是否灰度")
+ private Boolean greyReleased;
+
+ @Schema(description = "描述")
+ private String description;
+
+ @Schema(description = "认证策略编号")
+ private Long authPolicyId;
+
+ @Schema(description = "限流策略编号")
+ private Long rateLimitId;
+
+ @Schema(description = "响应模板(JSON)")
+ private String responseTemplate;
+
+ @Schema(description = "缓存策略(JSON)")
+ private String cacheStrategy;
+
+ @Schema(description = "创建时间")
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新时间")
+ private LocalDateTime updateTime;
+
+ @Schema(description = "创建人")
+ private String creator;
+
+ @Schema(description = "更新人")
+ private String updater;
+
+ @Schema(description = "API 级别变换列表")
+ private List apiLevelTransforms = new ArrayList<>();
+
+ @Schema(description = "步骤列表")
+ private List steps = new ArrayList<>();
+
+ @Schema(description = "发布信息")
+ private ApiDefinitionPublicationRespVO publication;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPageReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPageReqVO.java
new file mode 100644
index 00000000..cf5d82f4
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPageReqVO.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import com.zt.plat.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "管理后台 - API 定义分页查询 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiDefinitionPageReqVO extends PageParam {
+
+ @Schema(description = "关键字,匹配编码/描述/URI", example = "order")
+ private String keyword;
+
+ @Schema(description = "API 状态", example = "1")
+ private Integer status;
+
+ @Schema(description = "HTTP 方法", example = "POST")
+ private String httpMethod;
+
+ @Schema(description = "是否灰度", example = "true")
+ private Boolean greyReleased;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPublicationRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPublicationRespVO.java
new file mode 100644
index 00000000..b53b4c36
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionPublicationRespVO.java
@@ -0,0 +1,28 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "管理后台 - API 发布信息 Response VO")
+public class ApiDefinitionPublicationRespVO {
+
+ @Schema(description = "发布记录主键", example = "4001")
+ private Long id;
+
+ @Schema(description = "发布标签", example = "release-20231001")
+ private String releaseTag;
+
+ @Schema(description = "快照内容(JSON)")
+ private String snapshot;
+
+ @Schema(description = "状态", example = "RELEASED")
+ private String status;
+
+ @Schema(description = "是否当前生效")
+ private Boolean active;
+
+ @Schema(description = "描述")
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSaveReqVO.java
new file mode 100644
index 00000000..f9239581
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSaveReqVO.java
@@ -0,0 +1,67 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@Schema(description = "管理后台 - API 定义保存 Request VO")
+public class ApiDefinitionSaveReqVO {
+
+ @Schema(description = "主键", example = "1001")
+ private Long id;
+
+ @Schema(description = "API 编码", example = "order.create")
+ @NotBlank(message = "API 编码不能为空")
+ private String apiCode;
+
+ @Schema(description = "API 版本", example = "v1")
+ @NotBlank(message = "API 版本不能为空")
+ private String version;
+
+ @Schema(description = "HTTP 方法", example = "POST")
+ @NotBlank(message = "HTTP 方法不能为空")
+ private String httpMethod;
+
+ @Schema(description = "URI 模板", example = "/external/order/create")
+ @NotBlank(message = "URI 模板不能为空")
+ private String uriPattern;
+
+ @Schema(description = "API 状态", example = "1")
+ @NotNull(message = "API 状态不能为空")
+ private Integer status;
+
+ @Schema(description = "描述")
+ private String description;
+
+ @Schema(description = "认证策略编号")
+ private Long authPolicyId;
+
+ @Schema(description = "限流策略编号")
+ private Long rateLimitId;
+
+ @Schema(description = "响应模板(JSON)")
+ private String responseTemplate;
+
+ @Schema(description = "缓存策略(JSON)")
+ private String cacheStrategy;
+
+ @Schema(description = "是否开启灰度发布")
+ private Boolean greyReleased;
+
+ @Schema(description = "API 级别变换列表")
+ @Valid
+ private List apiLevelTransforms = new ArrayList<>();
+
+ @Schema(description = "步骤列表")
+ @NotEmpty(message = "编排步骤不能为空")
+ @Valid
+ private List steps = new ArrayList<>();
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepRespVO.java
new file mode 100644
index 00000000..e6f7b71d
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepRespVO.java
@@ -0,0 +1,55 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@Schema(description = "管理后台 - API 编排步骤详情 Response VO")
+public class ApiDefinitionStepRespVO {
+
+ @Schema(description = "步骤主键", example = "21001")
+ private Long id;
+
+ @Schema(description = "所属 API 主键", example = "1024")
+ private Long apiId;
+
+ @Schema(description = "步骤序号", example = "1")
+ private Integer stepOrder;
+
+ @Schema(description = "并行分组")
+ private String parallelGroup;
+
+ @Schema(description = "步骤类型", example = "HTTP")
+ private String type;
+
+ @Schema(description = "目标端点")
+ private String targetEndpoint;
+
+ @Schema(description = "请求映射表达式(JSON)")
+ private String requestMappingExpr;
+
+ @Schema(description = "响应映射表达式(JSON)")
+ private String responseMappingExpr;
+
+ @Schema(description = "超时时间(毫秒)")
+ private Long timeout;
+
+ @Schema(description = "重试策略(JSON)")
+ private String retryStrategy;
+
+ @Schema(description = "降级策略(JSON)")
+ private String fallbackStrategy;
+
+ @Schema(description = "条件表达式")
+ private String conditionExpr;
+
+ @Schema(description = "是否出错终止")
+ private Boolean stopOnError;
+
+ @Schema(description = "步骤级变换列表")
+ private List transforms = new ArrayList<>();
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java
new file mode 100644
index 00000000..650430ac
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionStepSaveReqVO.java
@@ -0,0 +1,58 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@Schema(description = "管理后台 - API 编排步骤保存 Request VO")
+public class ApiDefinitionStepSaveReqVO {
+
+ @Schema(description = "步骤主键", example = "21001")
+ private Long id;
+
+ @Schema(description = "步骤序号", example = "1")
+ @NotNull(message = "步骤序号不能为空")
+ private Integer stepOrder;
+
+ @Schema(description = "并行分组")
+ private String parallelGroup;
+
+ @Schema(description = "步骤类型", example = "HTTP")
+ @NotBlank(message = "步骤类型不能为空")
+ private String type;
+
+ @Schema(description = "目标端点", example = "https://api.demo.com/order")
+ private String targetEndpoint;
+
+ @Schema(description = "请求映射表达式(JSON)")
+ private String requestMappingExpr;
+
+ @Schema(description = "响应映射表达式(JSON)")
+ private String responseMappingExpr;
+
+ @Schema(description = "超时时间(毫秒)", example = "5000")
+ private Long timeout;
+
+ @Schema(description = "重试策略(JSON)")
+ private String retryStrategy;
+
+ @Schema(description = "降级策略(JSON)")
+ private String fallbackStrategy;
+
+ @Schema(description = "条件表达式")
+ private String conditionExpr;
+
+ @Schema(description = "是否出错终止")
+ private Boolean stopOnError;
+
+ @Schema(description = "步骤级变换列表")
+ @Valid
+ private List transforms = new ArrayList<>();
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSummaryRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSummaryRespVO.java
new file mode 100644
index 00000000..75e528cb
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionSummaryRespVO.java
@@ -0,0 +1,48 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@Schema(description = "管理后台 - API 定义分页列表 Response VO")
+public class ApiDefinitionSummaryRespVO {
+
+ @Schema(description = "主键", example = "1024")
+ private Long id;
+
+ @Schema(description = "API 编码", example = "order.create")
+ private String apiCode;
+
+ @Schema(description = "API 版本", example = "v1")
+ private String version;
+
+ @Schema(description = "HTTP 方法", example = "POST")
+ private String httpMethod;
+
+ @Schema(description = "URI 模板", example = "/external/order/create")
+ private String uriPattern;
+
+ @Schema(description = "状态", example = "1")
+ private Integer status;
+
+ @Schema(description = "是否灰度", example = "true")
+ private Boolean greyReleased;
+
+ @Schema(description = "描述")
+ private String description;
+
+ @Schema(description = "创建时间")
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新时间")
+ private LocalDateTime updateTime;
+
+ @Schema(description = "创建人")
+ private String creator;
+
+ @Schema(description = "更新人")
+ private String updater;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformRespVO.java
new file mode 100644
index 00000000..83c27912
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformRespVO.java
@@ -0,0 +1,31 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "管理后台 - API 变换详情 Response VO")
+public class ApiDefinitionTransformRespVO {
+
+ @Schema(description = "变换主键", example = "31001")
+ private Long id;
+
+ @Schema(description = "所属 API 主键", example = "1024")
+ private Long apiId;
+
+ @Schema(description = "所属步骤主键", example = "21001")
+ private Long stepId;
+
+ @Schema(description = "阶段", example = "REQUEST")
+ private String phase;
+
+ @Schema(description = "表达式类型", example = "SPEL")
+ private String expressionType;
+
+ @Schema(description = "表达式内容", example = "#{payload}")
+ private String expression;
+
+ @Schema(description = "描述")
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformSaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformSaveReqVO.java
new file mode 100644
index 00000000..bd33c184
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/definition/ApiDefinitionTransformSaveReqVO.java
@@ -0,0 +1,29 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.definition;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+@Schema(description = "管理后台 - API 变换保存 Request VO")
+public class ApiDefinitionTransformSaveReqVO {
+
+ @Schema(description = "变换主键", example = "31001")
+ private Long id;
+
+ @Schema(description = "阶段", example = "REQUEST")
+ @NotBlank(message = "变换阶段不能为空")
+ private String phase;
+
+ @Schema(description = "表达式类型", example = "SPEL")
+ @NotBlank(message = "表达式类型不能为空")
+ private String expressionType;
+
+ @Schema(description = "表达式内容", example = "#{payload}")
+ @NotBlank(message = "表达式内容不能为空")
+ private String expression;
+
+ @Schema(description = "描述")
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyBaseVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyBaseVO.java
new file mode 100644
index 00000000..e0c82b25
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyBaseVO.java
@@ -0,0 +1,27 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.policy;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * Base VO for policy definitions shared by request/response objects.
+ */
+@Data
+public class ApiPolicyBaseVO {
+
+ @Schema(description = "策略名称", example = "JWT")
+ @NotBlank(message = "策略名称不能为空")
+ private String name;
+
+ @Schema(description = "策略类型", example = "JWT")
+ @NotBlank(message = "策略类型不能为空")
+ private String type;
+
+ @Schema(description = "策略配置(JSON)", example = "{\"issuer\":\"iam\"}")
+ private String config;
+
+ @Schema(description = "策略描述", example = "JWT 认证策略")
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyPageReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyPageReqVO.java
new file mode 100644
index 00000000..77ee7cfe
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyPageReqVO.java
@@ -0,0 +1,22 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.policy;
+
+import com.zt.plat.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Policy search conditions with pagination.
+ */
+@Schema(description = "管理后台 - 策略分页查询 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiPolicyPageReqVO extends PageParam {
+
+ @Schema(description = "关键字(名称/描述)", example = "JWT")
+ private String keyword;
+
+ @Schema(description = "策略类型", example = "JWT")
+ private String type;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyRespVO.java
new file mode 100644
index 00000000..86ac74a5
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicyRespVO.java
@@ -0,0 +1,32 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.policy;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * Policy detail response VO.
+ */
+@Schema(description = "管理后台 - 策略详情 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiPolicyRespVO extends ApiPolicyBaseVO {
+
+ @Schema(description = "策略编号", example = "1024")
+ private Long id;
+
+ @Schema(description = "创建人", example = "admin")
+ private String creator;
+
+ @Schema(description = "修改人", example = "admin")
+ private String updater;
+
+ @Schema(description = "创建时间")
+ private LocalDateTime createTime;
+
+ @Schema(description = "最后更新时间")
+ private LocalDateTime updateTime;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySaveReqVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySaveReqVO.java
new file mode 100644
index 00000000..f8fb32ea
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySaveReqVO.java
@@ -0,0 +1,18 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.policy;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Policy create/update request VO.
+ */
+@Schema(description = "管理后台 - 策略保存 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiPolicySaveReqVO extends ApiPolicyBaseVO {
+
+ @Schema(description = "策略编号", example = "1024")
+ private Long id;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySimpleRespVO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySimpleRespVO.java
new file mode 100644
index 00000000..783f878d
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/controller/admin/gateway/vo/policy/ApiPolicySimpleRespVO.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.controller.admin.gateway.vo.policy;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * Policy simple response VO used by dropdowns.
+ */
+@Schema(description = "管理后台 - 策略精简 Response VO")
+@Data
+public class ApiPolicySimpleRespVO {
+
+ @Schema(description = "策略编号", example = "1024")
+ private Long id;
+
+ @Schema(description = "策略名称", example = "JWT")
+ private String name;
+
+ @Schema(description = "策略类型", example = "JWT")
+ private String type;
+
+ @Schema(description = "策略描述")
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiDefinitionDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiDefinitionDO.java
new file mode 100644
index 00000000..596bb40d
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiDefinitionDO.java
@@ -0,0 +1,52 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * API definition data object describing external API metadata and policies.
+ */
+@TableName("databus_api_definition")
+@KeySequence("databus_api_definition_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiDefinitionDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private String apiCode;
+
+ private String uriPattern;
+
+ private String httpMethod;
+
+ private String version;
+
+ /**
+ * API status, see {@code ApiPublishStatusEnum}.
+ */
+ private Integer status;
+
+ private String description;
+
+ private Long authPolicyId;
+
+ private Long rateLimitId;
+
+ private String responseTemplate;
+
+ private String cacheStrategy;
+
+ private LocalDateTime updatedAt;
+
+ private Boolean greyReleased;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiFlowPublishDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiFlowPublishDO.java
new file mode 100644
index 00000000..f16f1530
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiFlowPublishDO.java
@@ -0,0 +1,35 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Publication record for API flow snapshots and gray releases.
+ */
+@TableName("databus_api_flow_publish")
+@KeySequence("databus_api_flow_publish_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiFlowPublishDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private Long apiId;
+
+ private String releaseTag;
+
+ private String snapshot;
+
+ private String status;
+
+ private Boolean active;
+
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyAuthDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyAuthDO.java
new file mode 100644
index 00000000..90054af6
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyAuthDO.java
@@ -0,0 +1,31 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Authentication policy definition.
+ */
+@TableName("databus_policy_auth")
+@KeySequence("databus_policy_auth_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiPolicyAuthDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private String name;
+
+ private String type;
+
+ private String config;
+
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyRateLimitDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyRateLimitDO.java
new file mode 100644
index 00000000..b14cdafa
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiPolicyRateLimitDO.java
@@ -0,0 +1,31 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Rate limit policy definition stored in database.
+ */
+@TableName("databus_policy_rate_limit")
+@KeySequence("databus_policy_rate_limit_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiPolicyRateLimitDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private String name;
+
+ private String type;
+
+ private String config;
+
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiStepDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiStepDO.java
new file mode 100644
index 00000000..b0f17529
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiStepDO.java
@@ -0,0 +1,49 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * API orchestration step definition.
+ */
+@TableName("databus_api_step")
+@KeySequence("databus_api_step_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiStepDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private Long apiId;
+
+ private Integer stepOrder;
+
+ private String parallelGroup;
+
+ private String type;
+
+ private String targetEndpoint;
+
+ private String requestMappingExpr;
+
+ private String responseMappingExpr;
+
+ private Long transformId;
+
+ private Long timeout;
+
+ private String retryStrategy;
+
+ private String fallbackStrategy;
+
+ private String conditionExpr;
+
+ private Boolean stopOnError;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiTransformDO.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiTransformDO.java
new file mode 100644
index 00000000..e3ae2a2e
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/dataobject/gateway/ApiTransformDO.java
@@ -0,0 +1,35 @@
+package com.zt.plat.module.databus.dal.dataobject.gateway;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * API request/response transformation expressions.
+ */
+@TableName("databus_api_transform")
+@KeySequence("databus_api_transform_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ApiTransformDO extends TenantBaseDO {
+
+ @TableId(type = IdType.ASSIGN_ID)
+ private Long id;
+
+ private Long apiId;
+
+ private Long stepId;
+
+ private String phase;
+
+ private String expressionType;
+
+ private String expression;
+
+ private String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiDefinitionMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiDefinitionMapper.java
new file mode 100644
index 00000000..d4278f4a
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiDefinitionMapper.java
@@ -0,0 +1,63 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import cn.hutool.core.util.StrUtil;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionPageReqVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiDefinitionDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+import java.util.Optional;
+
+@Mapper
+public interface ApiDefinitionMapper extends BaseMapperX {
+
+ default Optional selectByCodeAndVersion(String apiCode, String version) {
+ return Optional.ofNullable(selectOne(ApiDefinitionDO::getApiCode, apiCode,
+ ApiDefinitionDO::getVersion, version,
+ ApiDefinitionDO::getDeleted, false));
+ }
+
+ default List selectActiveDefinitions(List statusList) {
+ return selectList(new LambdaQueryWrapperX()
+ .inIfPresent(ApiDefinitionDO::getStatus, statusList)
+ .eq(ApiDefinitionDO::getDeleted, false));
+ }
+
+ default PageResult selectPage(ApiDefinitionPageReqVO reqVO) {
+ LambdaQueryWrapperX query = new LambdaQueryWrapperX<>();
+ if (StrUtil.isNotBlank(reqVO.getKeyword())) {
+ String keyword = reqVO.getKeyword();
+ query.and(wrapper -> wrapper.like(ApiDefinitionDO::getApiCode, keyword)
+ .or().like(ApiDefinitionDO::getDescription, keyword)
+ .or().like(ApiDefinitionDO::getUriPattern, keyword));
+ }
+ query.eqIfPresent(ApiDefinitionDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(ApiDefinitionDO::getHttpMethod, reqVO.getHttpMethod())
+// .eqIfPresent(ApiDefinitionDO::getGreyReleased, reqVO.getGreyReleased())
+ .orderByDesc(ApiDefinitionDO::getUpdateTime)
+ .orderByDesc(ApiDefinitionDO::getId);
+ return selectPage(reqVO, query);
+ }
+
+ default Long selectCountByAuthPolicyId(Long policyId) {
+ if (policyId == null) {
+ return 0L;
+ }
+ return selectCount(new LambdaQueryWrapperX()
+ .eq(ApiDefinitionDO::getAuthPolicyId, policyId)
+ .eq(ApiDefinitionDO::getDeleted, false));
+ }
+
+ default Long selectCountByRateLimitPolicyId(Long policyId) {
+ if (policyId == null) {
+ return 0L;
+ }
+ return selectCount(new LambdaQueryWrapperX()
+ .eq(ApiDefinitionDO::getRateLimitId, policyId)
+ .eq(ApiDefinitionDO::getDeleted, false));
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiFlowPublishMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiFlowPublishMapper.java
new file mode 100644
index 00000000..02f47834
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiFlowPublishMapper.java
@@ -0,0 +1,19 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiFlowPublishDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Optional;
+
+@Mapper
+public interface ApiFlowPublishMapper extends BaseMapperX {
+
+ default Optional selectActiveByApiId(Long apiId) {
+ return Optional.ofNullable(selectOne(new LambdaQueryWrapperX()
+ .eq(ApiFlowPublishDO::getApiId, apiId)
+ .eq(ApiFlowPublishDO::getActive, true)));
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyAuthMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyAuthMapper.java
new file mode 100644
index 00000000..b1bf14ba
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyAuthMapper.java
@@ -0,0 +1,36 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import cn.hutool.core.util.StrUtil;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyPageReqVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyAuthDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ApiPolicyAuthMapper extends BaseMapperX {
+
+ default PageResult selectPage(ApiPolicyPageReqVO reqVO) {
+ LambdaQueryWrapperX query = new LambdaQueryWrapperX<>();
+ if (StrUtil.isNotBlank(reqVO.getKeyword())) {
+ String keyword = reqVO.getKeyword();
+ query.and(wrapper -> wrapper.like(ApiPolicyAuthDO::getName, keyword)
+ .or().like(ApiPolicyAuthDO::getDescription, keyword));
+ }
+ query.eqIfPresent(ApiPolicyAuthDO::getType, reqVO.getType())
+ .eq(ApiPolicyAuthDO::getDeleted, false)
+ .orderByDesc(ApiPolicyAuthDO::getUpdateTime)
+ .orderByDesc(ApiPolicyAuthDO::getId);
+ return selectPage(reqVO, query);
+ }
+
+ default List selectSimpleList() {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiPolicyAuthDO::getDeleted, false)
+ .orderByDesc(ApiPolicyAuthDO::getUpdateTime)
+ .orderByDesc(ApiPolicyAuthDO::getId));
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyRateLimitMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyRateLimitMapper.java
new file mode 100644
index 00000000..7d8ab89d
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiPolicyRateLimitMapper.java
@@ -0,0 +1,36 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import cn.hutool.core.util.StrUtil;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.controller.admin.gateway.vo.policy.ApiPolicyPageReqVO;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiPolicyRateLimitDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ApiPolicyRateLimitMapper extends BaseMapperX {
+
+ default PageResult selectPage(ApiPolicyPageReqVO reqVO) {
+ LambdaQueryWrapperX query = new LambdaQueryWrapperX<>();
+ if (StrUtil.isNotBlank(reqVO.getKeyword())) {
+ String keyword = reqVO.getKeyword();
+ query.and(wrapper -> wrapper.like(ApiPolicyRateLimitDO::getName, keyword)
+ .or().like(ApiPolicyRateLimitDO::getDescription, keyword));
+ }
+ query.eqIfPresent(ApiPolicyRateLimitDO::getType, reqVO.getType())
+ .eq(ApiPolicyRateLimitDO::getDeleted, false)
+ .orderByDesc(ApiPolicyRateLimitDO::getUpdateTime)
+ .orderByDesc(ApiPolicyRateLimitDO::getId);
+ return selectPage(reqVO, query);
+ }
+
+ default List selectSimpleList() {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiPolicyRateLimitDO::getDeleted, false)
+ .orderByDesc(ApiPolicyRateLimitDO::getUpdateTime)
+ .orderByDesc(ApiPolicyRateLimitDO::getId));
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiStepMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiStepMapper.java
new file mode 100644
index 00000000..d6f79e64
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiStepMapper.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiStepDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ApiStepMapper extends BaseMapperX {
+
+ default List selectByApiId(Long apiId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiStepDO::getApiId, apiId)
+ .orderByAsc(ApiStepDO::getParallelGroup)
+ .orderByAsc(ApiStepDO::getStepOrder));
+ }
+
+ default void deleteByApiId(Long apiId) {
+ delete(new LambdaQueryWrapperX()
+ .eq(ApiStepDO::getApiId, apiId));
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiTransformMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiTransformMapper.java
new file mode 100644
index 00000000..58a965d9
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/dal/mysql/gateway/ApiTransformMapper.java
@@ -0,0 +1,39 @@
+package com.zt.plat.module.databus.dal.mysql.gateway;
+
+import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiTransformDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ApiTransformMapper extends BaseMapperX {
+
+ default List selectByApiId(Long apiId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiTransformDO::getApiId, apiId));
+ }
+
+ default List selectByStepId(Long stepId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiTransformDO::getStepId, stepId));
+ }
+
+ default List selectApiLevelTransforms(Long apiId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ApiTransformDO::getApiId, apiId)
+ .isNull(ApiTransformDO::getStepId));
+ }
+
+ default void deleteByApiId(Long apiId) {
+ delete(new LambdaQueryWrapperX()
+ .eq(ApiTransformDO::getApiId, apiId));
+ }
+
+ default void deleteByStepId(Long stepId) {
+ delete(new LambdaQueryWrapperX()
+ .eq(ApiTransformDO::getStepId, stepId));
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStatusEnum.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStatusEnum.java
new file mode 100644
index 00000000..76ffda64
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStatusEnum.java
@@ -0,0 +1,28 @@
+package com.zt.plat.module.databus.enums.gateway;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * External API publish status enumeration.
+ */
+@AllArgsConstructor
+@Getter
+public enum ApiStatusEnum {
+
+ DRAFT(0),
+ ONLINE(1),
+ OFFLINE(2),
+ DEPRECATED(3);
+
+ private final int status;
+
+ public static boolean isOnline(Integer status) {
+ return status != null && status == ONLINE.status;
+ }
+
+ public static boolean isDeprecated(Integer status) {
+ return status != null && status == DEPRECATED.status;
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStepTypeEnum.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStepTypeEnum.java
new file mode 100644
index 00000000..589a49f1
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ApiStepTypeEnum.java
@@ -0,0 +1,18 @@
+package com.zt.plat.module.databus.enums.gateway;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Step types supported by the unified API portal.
+ */
+@AllArgsConstructor
+@Getter
+public enum ApiStepTypeEnum {
+
+ HTTP,
+ RPC,
+ SCRIPT,
+ FLOW;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ExpressionTypeEnum.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ExpressionTypeEnum.java
new file mode 100644
index 00000000..7efe85da
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/ExpressionTypeEnum.java
@@ -0,0 +1,32 @@
+package com.zt.plat.module.databus.enums.gateway;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Locale;
+
+/**
+ * Supported expression languages for request/response mapping.
+ */
+@AllArgsConstructor
+@Getter
+public enum ExpressionTypeEnum {
+
+ JSON("json");
+
+ private final String code;
+
+ public static ExpressionTypeEnum fromValue(String value) {
+ if (value == null) {
+ return null;
+ }
+ String normalized = value.trim().toLowerCase(Locale.ROOT);
+ for (ExpressionTypeEnum type : values()) {
+ if (type.code.equals(normalized) || type.name().equals(normalized.toUpperCase(Locale.ROOT))) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/TransformPhaseEnum.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/TransformPhaseEnum.java
new file mode 100644
index 00000000..6ea21521
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/enums/gateway/TransformPhaseEnum.java
@@ -0,0 +1,28 @@
+package com.zt.plat.module.databus.enums.gateway;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Transformation phase enumeration.
+ */
+@AllArgsConstructor
+@Getter
+public enum TransformPhaseEnum {
+
+ REQUEST_PRE,
+ REQUEST_POST,
+ RESPONSE_PRE,
+ RESPONSE_POST,
+ ERROR;
+
+ public static TransformPhaseEnum fromCode(String code) {
+ for (TransformPhaseEnum phase : values()) {
+ if (phase.name().equalsIgnoreCase(code)) {
+ return phase;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ApiGatewayProperties.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ApiGatewayProperties.java
new file mode 100644
index 00000000..ac37137a
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ApiGatewayProperties.java
@@ -0,0 +1,36 @@
+package com.zt.plat.module.databus.framework.integration.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Configuration properties for the unified API portal.
+ */
+@Data
+@ConfigurationProperties(prefix = "databus.api-portal")
+public class ApiGatewayProperties {
+
+ private String basePath = "/api/portal";
+
+ private List allowedIps = new ArrayList<>();
+
+ private List deniedIps = new ArrayList<>();
+
+ private boolean enableSignature = false;
+
+ private String signatureHeader = "X-Signature";
+
+ private String signatureSecret;
+
+ private boolean enableTenantHeader = true;
+
+ private String tenantHeader = "X-Tenant-Id";
+
+ private boolean enableAudit = true;
+
+ private boolean enableRateLimit = true;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ExpressionConfiguration.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ExpressionConfiguration.java
new file mode 100644
index 00000000..0096592d
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/ExpressionConfiguration.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.framework.integration.config;
+
+import com.zt.plat.module.databus.enums.gateway.ExpressionTypeEnum;
+import com.zt.plat.module.databus.framework.integration.gateway.expression.ExpressionEvaluatorRegistry;
+import com.zt.plat.module.databus.framework.integration.gateway.expression.JsonataExpressionEvaluator;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Registers expression evaluators with the registry.
+ */
+@Configuration
+@RequiredArgsConstructor
+public class ExpressionConfiguration {
+
+ private final ExpressionEvaluatorRegistry registry;
+ private final JsonataExpressionEvaluator jsonataExpressionEvaluator;
+
+ @PostConstruct
+ public void registerEvaluators() {
+ registry.register(ExpressionTypeEnum.JSON, jsonataExpressionEvaluator);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/GatewayIntegrationConfiguration.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/GatewayIntegrationConfiguration.java
new file mode 100644
index 00000000..9d75d547
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/config/GatewayIntegrationConfiguration.java
@@ -0,0 +1,147 @@
+package com.zt.plat.module.databus.framework.integration.config;
+
+import com.zt.plat.framework.common.exception.ServiceException;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ApiFlowDispatcher;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ApiGatewayRequestMapper;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ErrorHandlingStrategy;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.framework.integration.gateway.security.GatewaySecurityFilter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.integration.core.MessagingTemplate;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.http.dsl.Http;
+import org.springframework.integration.support.MessageBuilder;
+import org.springframework.messaging.Message;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Configures the unified API portal inbound gateway and supporting beans.
+ */
+@Slf4j
+@Configuration
+@EnableConfigurationProperties(ApiGatewayProperties.class)
+@RequiredArgsConstructor
+public class GatewayIntegrationConfiguration {
+
+ private final ApiGatewayProperties properties;
+ private final ApiGatewayRequestMapper requestMapper;
+ private final ObjectProvider apiFlowDispatcherProvider;
+ private final ErrorHandlingStrategy errorHandlingStrategy;
+
+ @Bean(name = "apiPortalTaskExecutor")
+ public ThreadPoolTaskExecutor apiPortalTaskExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(8);
+ executor.setMaxPoolSize(32);
+ executor.setQueueCapacity(256);
+ executor.setThreadNamePrefix("api-portal-");
+ executor.initialize();
+ return executor;
+ }
+
+ @Bean
+ public MessagingTemplate apiPortalMessagingTemplate() {
+ return new MessagingTemplate();
+ }
+
+ @Bean
+ public FilterRegistrationBean gatewaySecurityFilterRegistration(GatewaySecurityFilter filter) {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>(filter);
+ registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
+ return registration;
+ }
+
+ @Bean
+ public IntegrationFlow apiGatewayInboundFlow() {
+ String pattern = properties.getBasePath() + "/{apiCode}/{version}";
+ return IntegrationFlow.from(Http.inboundGateway(pattern)
+ .requestMapping(spec -> spec
+ .methods(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH))
+ .errorChannel(errorHandlingStrategy.getErrorChannel())
+ .requestPayloadType(String.class)
+ .mappedRequestHeaders("*")
+ .mappedResponseHeaders("*"))
+ .handle(this, "mapRequest", endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()))
+ .handle(this, "dispatch", endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()))
+ .handle(this, "buildResponse", endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()))
+ .get();
+ }
+
+ public Message mapRequest(Message> message) {
+ ApiInvocationContext context = requestMapper.map(message.getPayload(), message.getHeaders());
+ return MessageBuilder.withPayload(context)
+ .copyHeaders(message.getHeaders())
+ .setHeaderIfAbsent("apiCode", context.getApiCode())
+ .setHeaderIfAbsent("version", context.getApiVersion())
+ .build();
+ }
+
+ public ApiInvocationContext dispatch(Message message) {
+ ApiInvocationContext context = message.getPayload();
+ try {
+ return apiFlowDispatcherProvider.getObject()
+ .dispatch(context.getApiCode(), context.getApiVersion(), context);
+ } catch (ServiceException ex) {
+ handleServiceException(context, ex);
+ log.warn("[API-PORTAL] ServiceException while dispatching apiCode={} version={}: {}", context.getApiCode(), context.getApiVersion(), ex.getMessage());
+ return context;
+ } catch (Exception ex) {
+ handleUnexpectedException(context, ex);
+ log.error("[API-PORTAL] Unexpected exception while dispatching apiCode={} version={}", context.getApiCode(), context.getApiVersion(), ex);
+ return context;
+ }
+ }
+
+ public ResponseEntity buildResponse(ApiInvocationContext context) {
+ int status = context.getResponseStatus() != null ? context.getResponseStatus() : HttpStatus.OK.value();
+ ApiGatewayResponse envelope = ApiGatewayResponse.builder()
+ .code(status >= 200 && status < 400 ? "SUCCESS" : "ERROR")
+ .message(StringUtils.hasText(context.getResponseMessage()) ? context.getResponseMessage() : HttpStatus.valueOf(status).getReasonPhrase())
+ .data(context.getResponseBody())
+ .traceId(context.getRequestId())
+ .build();
+ return ResponseEntity.status(status).body(envelope);
+ }
+
+ private void handleServiceException(ApiInvocationContext context, ServiceException ex) {
+ String message = StringUtils.hasText(ex.getMessage()) ? ex.getMessage() : "API invocation failed";
+ context.setResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
+ context.setResponseMessage(message);
+ if (context.getResponseBody() == null) {
+ Map body = new HashMap<>();
+ if (ex.getCode() != null) {
+ body.put("errorCode", ex.getCode());
+ }
+ body.put("errorMessage", message);
+ context.setResponseBody(body);
+ }
+ }
+
+ private void handleUnexpectedException(ApiInvocationContext context, Exception ex) {
+ String message = StringUtils.hasText(ex.getMessage()) ? ex.getMessage() : "API invocation encountered an unexpected error";
+ context.setResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
+ context.setResponseMessage(message);
+ if (context.getResponseBody() == null) {
+ Map body = new HashMap<>();
+ body.put("errorMessage", message);
+ body.put("exception", ex.getClass().getSimpleName());
+ context.setResponseBody(body);
+ }
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowAssembler.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowAssembler.java
new file mode 100644
index 00000000..a1d8c005
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowAssembler.java
@@ -0,0 +1,270 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.framework.common.exception.ServiceException;
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.module.databus.enums.gateway.ExpressionTypeEnum;
+import com.zt.plat.module.databus.enums.gateway.TransformPhaseEnum;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
+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.expression.ExpressionSpec;
+import com.zt.plat.module.databus.framework.integration.gateway.expression.ExpressionSpecParser;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.framework.integration.gateway.step.StepHandlerFactory;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.aop.Advice;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.integration.core.GenericHandler;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.IntegrationFlowBuilder;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.messaging.MessageHeaders;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_PARALLEL_FAILED;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_PARALLEL_INTERRUPTED;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_TRANSFORM_EVALUATION_FAILED;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_TRANSFORM_RESPONSE_STATUS_INVALID;
+
+/**
+ * Assembles dynamic integration flows per API definition.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ApiFlowAssembler {
+
+ private final StepHandlerFactory stepHandlerFactory;
+ private final PolicyAdvisorFactory policyAdvisorFactory;
+ private final ErrorHandlingStrategy errorHandlingStrategy;
+ private final MonitoringInterceptor monitoringInterceptor;
+ private final ExpressionExecutor expressionExecutor;
+ @Qualifier("apiPortalTaskExecutor")
+ private final TaskExecutor apiPortalTaskExecutor;
+
+ public ApiFlowRegistration assemble(ApiDefinitionAggregate aggregate) {
+ String inputChannelName = channelName(aggregate);
+ String flowId = flowId(aggregate);
+ IntegrationFlowBuilder builder = IntegrationFlow.from(MessageChannels.direct(inputChannelName)
+ .datatype(ApiInvocationContext.class)
+ .interceptor(monitoringInterceptor))
+ .log(message -> String.format("[API-PORTAL] entering flow %s", flowId))
+ .handle(ApiInvocationContext.class,
+ applyTransforms(aggregate, TransformPhaseEnum.REQUEST_PRE),
+ endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()));
+
+ List segments = segments(aggregate.getSteps());
+ for (FlowSegment segment : segments) {
+ if (segment instanceof SequentialSegment sequentialSegment) {
+ builder = applySequential(builder, aggregate, sequentialSegment.getStep());
+ } else if (segment instanceof ParallelSegment parallelSegment) {
+ builder = applyParallel(builder, aggregate, parallelSegment);
+ }
+ }
+
+ builder = builder
+ .handle(ApiInvocationContext.class,
+ applyTransforms(aggregate, TransformPhaseEnum.RESPONSE_PRE),
+ endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()))
+ .handle(ApiInvocationContext.class,
+ (payload, headers) -> payload,
+ endpoint -> endpoint.advice(errorHandlingStrategy.errorForwardingAdvice()));
+
+ return ApiFlowRegistration.builder()
+ .flowId(flowId)
+ .inputChannelName(inputChannelName)
+ .flow(builder.get())
+ .build();
+ }
+
+ private GenericHandler applyTransforms(ApiDefinitionAggregate aggregate, TransformPhaseEnum phase) {
+ return (payload, headers) -> {
+ var transformDefinition = aggregate.getApiLevelTransforms().get(phase.name());
+ if (transformDefinition != null && StringUtils.hasText(transformDefinition.getExpression())) {
+ String rawExpression = transformDefinition.getExpressionType() + "::" + transformDefinition.getExpression();
+ ExpressionSpec spec = ExpressionSpecParser.parse(rawExpression, ExpressionTypeEnum.JSON);
+ try {
+ Object result = expressionExecutor.evaluate(spec, payload, payload.getRequestBody(), headers);
+ applyTransformResult(payload, result);
+ } catch (Exception ex) {
+ if (ex instanceof ServiceException serviceException) {
+ throw serviceException;
+ }
+ throw ServiceExceptionUtil.exception(API_TRANSFORM_EVALUATION_FAILED, ex.getMessage());
+ }
+ }
+ return payload;
+ };
+ }
+
+ private void applyTransformResult(ApiInvocationContext context, Object result) {
+ if (!(result instanceof Map, ?> map)) {
+ return;
+ }
+ Object headerUpdates = map.get("requestHeaders");
+ if (headerUpdates instanceof Map, ?> headerMap) {
+ headerMap.forEach((key, value) -> context.getRequestHeaders().put(String.valueOf(key), value));
+ }
+ Object variableUpdates = map.get("variables");
+ if (variableUpdates instanceof Map, ?> variables) {
+ variables.forEach((key, value) -> context.getVariables().put(String.valueOf(key), value));
+ }
+ Object attributeUpdates = map.get("attributes");
+ if (attributeUpdates instanceof Map, ?> attributes) {
+ attributes.forEach((key, value) -> context.getAttributes().put(String.valueOf(key), value));
+ }
+ if (map.containsKey("responseBody")) {
+ context.setResponseBody(map.get("responseBody"));
+ }
+ if (map.containsKey("responseStatus")) {
+ context.setResponseStatus(asInteger(map.get("responseStatus")));
+ }
+ if (map.containsKey("responseMessage")) {
+ Object message = map.get("responseMessage");
+ context.setResponseMessage(message == null ? null : String.valueOf(message));
+ }
+ }
+
+ private Integer asInteger(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Number number) {
+ return number.intValue();
+ }
+ try {
+ return Integer.parseInt(String.valueOf(value));
+ } catch (NumberFormatException ex) {
+ throw ServiceExceptionUtil.exception(API_TRANSFORM_RESPONSE_STATUS_INVALID, value);
+ }
+ }
+
+ private IntegrationFlowBuilder applySequential(IntegrationFlowBuilder builder, ApiDefinitionAggregate aggregate, ApiStepDefinition stepDefinition) {
+ GenericHandler handler = stepHandlerFactory.build(aggregate, stepDefinition);
+ return builder.handle(ApiInvocationContext.class, handler, endpoint -> {
+ endpoint.advice(errorHandlingStrategy.errorForwardingAdvice());
+ Advice[] advices = policyAdvisorFactory.buildAdvices(aggregate, stepDefinition);
+ if (advices.length > 0) {
+ endpoint.advice(advices);
+ }
+ });
+ }
+
+ private IntegrationFlowBuilder applyParallel(IntegrationFlowBuilder builder, ApiDefinitionAggregate aggregate, ParallelSegment segment) {
+ return builder.handle(ApiInvocationContext.class,
+ (payload, headers) -> executeParallel(payload, headers, aggregate, segment),
+ endpoint -> {
+ endpoint.advice(errorHandlingStrategy.errorForwardingAdvice());
+ Advice[] advices = policyAdvisorFactory.buildParallelAdvices(aggregate, segment);
+ if (advices.length > 0) {
+ endpoint.advice(advices);
+ }
+ });
+ }
+
+ private ApiInvocationContext executeParallel(ApiInvocationContext context, MessageHeaders headers,
+ ApiDefinitionAggregate aggregate, ParallelSegment segment) {
+ List> futures = new ArrayList<>();
+ for (ApiStepDefinition step : segment.getSteps()) {
+ GenericHandler handler = stepHandlerFactory.build(aggregate, step);
+ ApiInvocationContext childContext = context.copy();
+ futures.add(CompletableFuture.supplyAsync(() -> {
+ handler.handle(childContext, headers);
+ return childContext;
+ }, apiPortalTaskExecutor));
+ }
+ for (CompletableFuture future : futures) {
+ try {
+ ApiInvocationContext child = future.get();
+ context.merge(child);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ throw ServiceExceptionUtil.exception(API_PARALLEL_INTERRUPTED);
+ } catch (ExecutionException ex) {
+ Throwable cause = ex.getCause();
+ if (cause instanceof ServiceException serviceException) {
+ throw serviceException;
+ }
+ throw ServiceExceptionUtil.exception(API_PARALLEL_FAILED, cause == null ? ex.getMessage() : cause.getMessage());
+ }
+ }
+ return context;
+ }
+
+ private List segments(List steps) {
+ return steps.stream()
+ .sorted(Comparator.comparingInt(step -> step.getStep().getStepOrder() == null ? Integer.MAX_VALUE : step.getStep().getStepOrder()))
+ .collect(ArrayList::new, this::consumeStep, this::combineSegments);
+ }
+
+ private void consumeStep(List segments, ApiStepDefinition step) {
+ String parallelGroup = step.getStep().getParallelGroup();
+ if (!StringUtils.hasText(parallelGroup)) {
+ segments.add(new SequentialSegment(step));
+ return;
+ }
+ FlowSegment last = segments.isEmpty() ? null : segments.get(segments.size() - 1);
+ if (last instanceof ParallelSegment parallelSegment && parallelGroup.equals(parallelSegment.getGroup())) {
+ parallelSegment.getSteps().add(step);
+ } else {
+ ParallelSegment newSegment = new ParallelSegment(parallelGroup, new ArrayList<>());
+ newSegment.getSteps().add(step);
+ segments.add(newSegment);
+ }
+ }
+
+ private void combineSegments(List target, List source) {
+ target.addAll(source);
+ }
+
+ private String channelName(ApiDefinitionAggregate aggregate) {
+ return "api.portal.flow." + aggregate.getDefinition().getApiCode().toLowerCase() + "." + aggregate.getDefinition().getVersion();
+ }
+
+ private String flowId(ApiDefinitionAggregate aggregate) {
+ return "apiPortalFlow:" + aggregate.getDefinition().getApiCode() + ":" + aggregate.getDefinition().getVersion();
+ }
+
+ private interface FlowSegment {
+ }
+
+ private static final class SequentialSegment implements FlowSegment {
+ private final ApiStepDefinition step;
+
+ private SequentialSegment(ApiStepDefinition step) {
+ this.step = step;
+ }
+
+ public ApiStepDefinition getStep() {
+ return step;
+ }
+ }
+
+ private static final class ParallelSegment implements FlowSegment {
+ private final String group;
+ private final List steps;
+
+ private ParallelSegment(String group, List steps) {
+ this.group = group;
+ this.steps = steps;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public List getSteps() {
+ return steps;
+ }
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowDispatcher.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowDispatcher.java
new file mode 100644
index 00000000..2484aaa2
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowDispatcher.java
@@ -0,0 +1,38 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.integration.core.MessagingTemplate;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Component;
+
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_FLOW_NOT_FOUND;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_FLOW_NO_REPLY;
+
+/**
+ * Dispatches API invocation contexts to the appropriate integration flow.
+ */
+@Component
+@RequiredArgsConstructor
+public class ApiFlowDispatcher {
+
+ private final IntegrationFlowManager integrationFlowManager;
+ private final MessagingTemplate messagingTemplate;
+
+ public ApiInvocationContext dispatch(String apiCode, String version, ApiInvocationContext context) {
+ MessageChannel channel = integrationFlowManager.locateInputChannel(apiCode, version)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_FLOW_NOT_FOUND, apiCode, version));
+ Message message = MessageBuilder.withPayload(context)
+ .setHeader("apiCode", apiCode)
+ .setHeader("version", version)
+ .build();
+ Message> reply = messagingTemplate.sendAndReceive(channel, message);
+ if (reply == null) {
+ throw ServiceExceptionUtil.exception(API_FLOW_NO_REPLY, apiCode, version);
+ }
+ return (ApiInvocationContext) reply.getPayload();
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowRegistration.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowRegistration.java
new file mode 100644
index 00000000..cdd57f1c
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiFlowRegistration.java
@@ -0,0 +1,20 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import lombok.Builder;
+import lombok.Value;
+import org.springframework.integration.dsl.IntegrationFlow;
+
+/**
+ * Metadata returned by the assembler for flow registration.
+ */
+@Value
+@Builder
+public class ApiFlowRegistration {
+
+ String flowId;
+
+ String inputChannelName;
+
+ IntegrationFlow flow;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java
new file mode 100644
index 00000000..c050f260
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java
@@ -0,0 +1,82 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerMapping;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Maps inbound HTTP request metadata into {@link ApiInvocationContext} instances.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ApiGatewayRequestMapper {
+
+ private final ObjectMapper objectMapper;
+ private final ApiGatewayProperties properties;
+
+ private static final String HEADER_REQUEST_HEADERS = org.springframework.integration.http.HttpHeaders.PREFIX + "requestHeaders";
+ private static final String HEADER_REQUEST_URI = org.springframework.integration.http.HttpHeaders.PREFIX + "requestUri";
+
+ @SuppressWarnings("unchecked")
+ public ApiInvocationContext map(Object payload, Map headers) {
+ ApiInvocationContext context = ApiInvocationContext.create();
+ Map uriVariables = (Map) headers.get(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ if (uriVariables != null) {
+ context.setApiCode(String.valueOf(uriVariables.get("apiCode")));
+ context.setApiVersion(String.valueOf(uriVariables.get("version")));
+ }
+ Object methodHeader = headers.get(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD);
+ if (methodHeader != null) {
+ context.setHttpMethod(String.valueOf(methodHeader));
+ }
+ Object requestPath = headers.get(HEADER_REQUEST_URI);
+ if (requestPath == null) {
+ requestPath = headers.get(org.springframework.integration.http.HttpHeaders.REQUEST_URL);
+ }
+ if (requestPath != null) {
+ context.setRequestPath(String.valueOf(requestPath));
+ }
+ Map requestHeaders = (Map) headers.get(HEADER_REQUEST_HEADERS);
+ if (requestHeaders != null) {
+ requestHeaders.forEach((key, value) -> context.getRequestHeaders().put(key, String.valueOf(value)));
+ }
+ if (properties.isEnableTenantHeader()) {
+ Object tenantHeaderValue = context.getRequestHeaders().get(properties.getTenantHeader());
+ if (tenantHeaderValue != null) {
+ context.setTenantId(String.valueOf(tenantHeaderValue));
+ }
+ }
+ if (payload instanceof String body) {
+ if (StringUtils.hasText(body) && isJsonContent(context)) {
+ try {
+ context.setRequestBody(objectMapper.readValue(body, Object.class));
+ } catch (IOException ex) {
+ log.warn("Failed to parse request body as JSON", ex);
+ context.setRequestBody(body);
+ }
+ } else {
+ context.setRequestBody(body);
+ }
+ } else {
+ context.setRequestBody(payload);
+ }
+ return context;
+ }
+
+ private boolean isJsonContent(ApiInvocationContext context) {
+ String contentType = String.valueOf(context.getRequestHeaders().getOrDefault(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)).toLowerCase(Locale.ROOT);
+ return contentType.contains(MediaType.APPLICATION_JSON_VALUE);
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ErrorHandlingStrategy.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ErrorHandlingStrategy.java
new file mode 100644
index 00000000..e7307110
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ErrorHandlingStrategy.java
@@ -0,0 +1,73 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.aop.Advice;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.integration.handler.advice.AbstractHandleMessageAdvice;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.support.ErrorMessage;
+import org.springframework.stereotype.Component;
+
+/**
+ * Centralized error channel and handler for the API portal.
+ */
+@Slf4j
+@Component
+public class ErrorHandlingStrategy {
+
+ @Getter
+ private final MessageChannel errorChannel;
+ private final Advice errorForwardingAdvice;
+
+ public ErrorHandlingStrategy() {
+ DirectChannel channel = MessageChannels.direct("apiPortalErrorChannel").getObject();
+ this.errorChannel = channel;
+ channel.subscribe(this::handleErrorMessage);
+ this.errorForwardingAdvice = new ErrorForwardingAdvice();
+ }
+
+ public Advice errorForwardingAdvice() {
+ return errorForwardingAdvice;
+ }
+
+ private void handleErrorMessage(Message> message) {
+ if (message instanceof ErrorMessage errorMessage) {
+ handleError(errorMessage);
+ }
+ }
+
+ private void handleError(ErrorMessage errorMessage) {
+ Throwable throwable = errorMessage.getPayload();
+ Message> failedMessage = errorMessage.getOriginalMessage();
+ if (failedMessage != null && failedMessage.getPayload() instanceof ApiInvocationContext context) {
+ context.setResponseStatus(500);
+ context.setResponseMessage(throwable.getMessage());
+ }
+ log.error("[API-PORTAL] Integration flow error", throwable);
+ }
+
+ private class ErrorForwardingAdvice extends AbstractHandleMessageAdvice {
+
+ @Override
+ protected Object doInvoke(MethodInvocation invocation, Message> message) throws Throwable {
+ try {
+ return invocation.proceed();
+ } catch (Throwable ex) {
+ ErrorMessage errorMessage = new ErrorMessage(ex, message);
+ try {
+ if (!errorChannel.send(errorMessage)) {
+ log.warn("[API-PORTAL] Failed to forward error message to channel {}", errorChannel);
+ }
+ } catch (Exception sendEx) {
+ log.error("[API-PORTAL] Error while submitting message to error channel", sendEx);
+ }
+ throw ex;
+ }
+ }
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/IntegrationFlowManager.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/IntegrationFlowManager.java
new file mode 100644
index 00000000..0867b24b
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/IntegrationFlowManager.java
@@ -0,0 +1,96 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
+import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.integration.dsl.context.IntegrationFlowContext;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Manages dynamic registration of API integration flows.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class IntegrationFlowManager {
+
+ private final IntegrationFlowContext integrationFlowContext;
+ private final ApiDefinitionService apiDefinitionService;
+ private final ApiFlowAssembler apiFlowAssembler;
+
+ private final Map activeRegistrations = new ConcurrentHashMap<>();
+
+ @PostConstruct
+ public void bootstrap() {
+ refreshAll();
+ }
+
+ public void refreshAll() {
+ List aggregates = apiDefinitionService.loadActiveDefinitions();
+ Map desired = new ConcurrentHashMap<>();
+ for (ApiDefinitionAggregate aggregate : aggregates) {
+ desired.put(key(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion()), aggregate);
+ }
+
+ // remove flows that are no longer active
+ activeRegistrations.keySet().stream()
+ .filter(existingKey -> !desired.containsKey(existingKey))
+ .forEach(this::deregisterByKey);
+
+ // register or refresh active flows
+ desired.values().forEach(this::registerFlow);
+ }
+
+ public void refresh(String apiCode, String version) {
+ apiDefinitionService.refresh(apiCode, version)
+ .ifPresentOrElse(this::registerFlow, () -> deregister(apiCode, version));
+ }
+
+ public Optional locateInputChannel(String apiCode, String version) {
+ String key = key(apiCode, version);
+ IntegrationFlowContext.IntegrationFlowRegistration registration = activeRegistrations.get(key);
+ if (registration == null) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(registration.getInputChannel());
+ }
+
+ private void registerFlow(ApiDefinitionAggregate aggregate) {
+ String key = key(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
+ deregisterByKey(key);
+ ApiFlowRegistration apiFlowRegistration = apiFlowAssembler.assemble(aggregate);
+ IntegrationFlowContext.IntegrationFlowRegistration registration = integrationFlowContext.registration(apiFlowRegistration.getFlow())
+ .id(apiFlowRegistration.getFlowId())
+ .register();
+ activeRegistrations.put(key, registration);
+ log.info("[API-PORTAL] registered flow {} for apiCode={} version={}", apiFlowRegistration.getFlowId(), aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
+ }
+
+ private void deregister(String apiCode, String version) {
+ deregisterByKey(key(apiCode, version));
+ }
+
+ private void deregisterByKey(String key) {
+ IntegrationFlowContext.IntegrationFlowRegistration existing = activeRegistrations.remove(key);
+ if (existing != null) {
+ try {
+ integrationFlowContext.remove(existing.getId());
+ log.info("[API-PORTAL] deregistered flow {} for key {}", existing.getId(), key);
+ } catch (Exception ex) {
+ log.warn("Failed to remove integration flow {}", existing.getId(), ex);
+ }
+ }
+ }
+
+ private String key(String apiCode, String version) {
+ return (apiCode + ":" + version).toLowerCase();
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/MonitoringInterceptor.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/MonitoringInterceptor.java
new file mode 100644
index 00000000..f2895a99
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/MonitoringInterceptor.java
@@ -0,0 +1,54 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * Channel interceptor capturing timing metrics and enriched logging.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class MonitoringInterceptor implements ChannelInterceptor {
+
+ private static final String HEADER_START_TIME = "ApiPortalStartTime";
+
+ private final MeterRegistry meterRegistry;
+
+ @Override
+ public Message> preSend(Message> message, MessageChannel channel) {
+ return MessageBuilder.fromMessage(message)
+ .setHeader(HEADER_START_TIME, Instant.now())
+ .build();
+ }
+
+ @Override
+ public void afterSendCompletion(Message> message, MessageChannel channel, boolean sent, Exception ex) {
+ Instant start = message.getHeaders().get(HEADER_START_TIME, Instant.class);
+ if (start != null) {
+ Duration duration = Duration.between(start, Instant.now());
+ Object payload = message.getPayload();
+ if (payload instanceof ApiInvocationContext context) {
+ Timer.builder("api.portal.latency")
+ .tag("api", context.getApiCode())
+ .tag("version", context.getApiVersion())
+ .register(meterRegistry)
+ .record(duration);
+ }
+ }
+ if (ex != null) {
+ log.error("[API-PORTAL] Channel send failed", ex);
+ }
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/PolicyAdvisorFactory.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/PolicyAdvisorFactory.java
new file mode 100644
index 00000000..37360507
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/PolicyAdvisorFactory.java
@@ -0,0 +1,166 @@
+package com.zt.plat.module.databus.framework.integration.gateway.core;
+
+import com.zt.plat.framework.common.exception.ServiceException;
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
+import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiStepDefinition;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.framework.integration.gateway.policy.AuthPolicyEvaluator;
+import com.zt.plat.module.databus.framework.integration.gateway.policy.RateLimitPolicyEvaluator;
+import lombok.RequiredArgsConstructor;
+import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
+import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
+import org.springframework.retry.backoff.ExponentialBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_STEP_EXECUTION_ERROR;
+
+/**
+ * Builds advice chains for steps based on configured policies.
+ */
+@Component
+@RequiredArgsConstructor
+public class PolicyAdvisorFactory {
+
+ private final AuthPolicyEvaluator authPolicyEvaluator;
+ private final RateLimitPolicyEvaluator rateLimitPolicyEvaluator;
+
+ public org.aopalliance.aop.Advice[] buildAdvices(ApiDefinitionAggregate aggregate, ApiStepDefinition stepDefinition) {
+ List advices = new ArrayList<>();
+ advices.add(new AuthPolicyAdvice(aggregate));
+ advices.add(new RateLimitPolicyAdvice(aggregate));
+ advices.add(createRetryAdvice(stepDefinition));
+ return advices.stream().filter(advice -> advice != null).toArray(org.aopalliance.aop.Advice[]::new);
+ }
+
+ public org.aopalliance.aop.Advice[] buildParallelAdvices(ApiDefinitionAggregate aggregate, Object segment) {
+ // For parallel segments we reuse the same advice chain (auth + rateLimit once at entry)
+ return buildAdvices(aggregate, null);
+ }
+
+ private RequestHandlerRetryAdvice createRetryAdvice(ApiStepDefinition stepDefinition) {
+ if (stepDefinition == null) {
+ return null;
+ }
+ Object strategyConfig = stepDefinition.getMetadata().get("retryStrategy");
+ if (!(strategyConfig instanceof Map, ?> configMap)) {
+ return null;
+ }
+ RetryTemplate template = new RetryTemplate();
+ SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
+ int maxAttempts = asInt(configMap.get("maxAttempts"), 3);
+ retryPolicy.setMaxAttempts(maxAttempts);
+ ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
+ long initialInterval = asLong(configMap.get("initialInterval"), 200L);
+ double multiplier = asDouble(configMap.get("multiplier"), 2.0d);
+ long maxInterval = asLong(configMap.get("maxInterval"), 2000L);
+ backOffPolicy.setInitialInterval(initialInterval);
+ backOffPolicy.setMultiplier(multiplier);
+ backOffPolicy.setMaxInterval(maxInterval);
+ template.setBackOffPolicy(backOffPolicy);
+ template.setRetryPolicy(retryPolicy);
+ RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
+ advice.setRetryTemplate(template);
+ return advice;
+ }
+
+ private final class AuthPolicyAdvice extends AbstractRequestHandlerAdvice {
+ private final ApiDefinitionAggregate aggregate;
+
+ private AuthPolicyAdvice(ApiDefinitionAggregate aggregate) {
+ this.aggregate = aggregate;
+ }
+
+ @Override
+ protected Object doInvoke(ExecutionCallback callback, Object target, org.springframework.messaging.Message> message) {
+ if (aggregate.getAuthPolicy() != null) {
+ authPolicyEvaluator.evaluate(aggregate, (ApiInvocationContext) message.getPayload());
+ }
+ try {
+ return callback.execute();
+ } catch (Exception ex) {
+ if (ex instanceof ServiceException serviceException) {
+ throw serviceException;
+ }
+ if (ex instanceof RuntimeException runtimeException) {
+ throw runtimeException;
+ }
+ throw ServiceExceptionUtil.exception(API_STEP_EXECUTION_ERROR, ex.getMessage());
+ }
+ }
+ }
+
+ private final class RateLimitPolicyAdvice extends AbstractRequestHandlerAdvice {
+ private final ApiDefinitionAggregate aggregate;
+
+ private RateLimitPolicyAdvice(ApiDefinitionAggregate aggregate) {
+ this.aggregate = aggregate;
+ }
+
+ @Override
+ protected Object doInvoke(ExecutionCallback callback, Object target, org.springframework.messaging.Message> message) {
+ if (aggregate.getRateLimitPolicy() != null) {
+ rateLimitPolicyEvaluator.evaluate(aggregate, (ApiInvocationContext) message.getPayload());
+ }
+ try {
+ return callback.execute();
+ } catch (Exception ex) {
+ if (ex instanceof ServiceException serviceException) {
+ throw serviceException;
+ }
+ if (ex instanceof RuntimeException runtimeException) {
+ throw runtimeException;
+ }
+ throw ServiceExceptionUtil.exception(API_STEP_EXECUTION_ERROR, ex.getMessage());
+ }
+ }
+ }
+
+ private int asInt(Object value, int defaultValue) {
+ if (value instanceof Number number) {
+ return number.intValue();
+ }
+ if (value instanceof String text) {
+ try {
+ return Integer.parseInt(text);
+ } catch (NumberFormatException ignored) {
+ // ignore and fall back to default
+ }
+ }
+ return defaultValue;
+ }
+
+ private long asLong(Object value, long defaultValue) {
+ if (value instanceof Number number) {
+ return number.longValue();
+ }
+ if (value instanceof String text) {
+ try {
+ return Long.parseLong(text);
+ } catch (NumberFormatException ignored) {
+ // ignore and fall back to default
+ }
+ }
+ return defaultValue;
+ }
+
+ private double asDouble(Object value, double defaultValue) {
+ if (value instanceof Number number) {
+ return number.doubleValue();
+ }
+ if (value instanceof String text) {
+ try {
+ return Double.parseDouble(text);
+ } catch (NumberFormatException ignored) {
+ // ignore and fall back to default
+ }
+ }
+ return defaultValue;
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiDefinitionAggregate.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiDefinitionAggregate.java
new file mode 100644
index 00000000..34a5bdef
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiDefinitionAggregate.java
@@ -0,0 +1,40 @@
+package com.zt.plat.module.databus.framework.integration.gateway.domain;
+
+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 lombok.Builder;
+import lombok.Value;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Aggregate representing an API definition with its steps and policies.
+ */
+@Value
+@Builder(toBuilder = true)
+public class ApiDefinitionAggregate {
+
+ ApiDefinitionDO definition;
+
+ List steps;
+
+ Map apiLevelTransforms;
+
+ ApiPolicyAuthDO authPolicy;
+
+ ApiPolicyRateLimitDO rateLimitPolicy;
+
+ ApiFlowPublication publication;
+
+ public List getSteps() {
+ return steps == null ? Collections.emptyList() : steps;
+ }
+
+ public Map getApiLevelTransforms() {
+ return apiLevelTransforms == null ? Collections.emptyMap() : apiLevelTransforms;
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiFlowPublication.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiFlowPublication.java
new file mode 100644
index 00000000..b3cba9e4
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiFlowPublication.java
@@ -0,0 +1,25 @@
+package com.zt.plat.module.databus.framework.integration.gateway.domain;
+
+import lombok.Builder;
+import lombok.Value;
+
+/**
+ * Publication metadata for an API flow.
+ */
+@Value
+@Builder(toBuilder = true)
+public class ApiFlowPublication {
+
+ Long id;
+
+ String releaseTag;
+
+ String snapshot;
+
+ String status;
+
+ boolean active;
+
+ String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiStepDefinition.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiStepDefinition.java
new file mode 100644
index 00000000..f25bed9b
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiStepDefinition.java
@@ -0,0 +1,32 @@
+package com.zt.plat.module.databus.framework.integration.gateway.domain;
+
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiStepDO;
+import lombok.Builder;
+import lombok.Value;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Domain representation of an orchestration step.
+ */
+@Value
+@Builder(toBuilder = true)
+public class ApiStepDefinition {
+
+ ApiStepDO step;
+
+ List transforms;
+
+ Map metadata;
+
+ public List getTransforms() {
+ return transforms == null ? Collections.emptyList() : transforms;
+ }
+
+ public Map getMetadata() {
+ return metadata == null ? Collections.emptyMap() : metadata;
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiTransformDefinition.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiTransformDefinition.java
new file mode 100644
index 00000000..3bc0edad
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/domain/ApiTransformDefinition.java
@@ -0,0 +1,23 @@
+package com.zt.plat.module.databus.framework.integration.gateway.domain;
+
+import lombok.Builder;
+import lombok.Value;
+
+/**
+ * Domain representation for transformation expression metadata.
+ */
+@Value
+@Builder(toBuilder = true)
+public class ApiTransformDefinition {
+
+ Long id;
+
+ String phase;
+
+ String expressionType;
+
+ String expression;
+
+ String description;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluationContext.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluationContext.java
new file mode 100644
index 00000000..fd857cb1
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluationContext.java
@@ -0,0 +1,24 @@
+package com.zt.plat.module.databus.framework.integration.gateway.expression;
+
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import lombok.Builder;
+import lombok.Value;
+
+import java.util.Map;
+
+/**
+ * Context provided to expression engines when evaluating mappings.
+ */
+@Value
+@Builder
+public class ExpressionEvaluationContext {
+
+ ApiInvocationContext invocation;
+
+ Object payload;
+
+ Map variables;
+
+ Map headers;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluator.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluator.java
new file mode 100644
index 00000000..ba436115
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluator.java
@@ -0,0 +1,10 @@
+package com.zt.plat.module.databus.framework.integration.gateway.expression;
+
+/**
+ * Expression evaluator contract.
+ */
+public interface ExpressionEvaluator {
+
+ Object evaluate(String expression, ExpressionEvaluationContext context) throws Exception;
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluatorRegistry.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluatorRegistry.java
new file mode 100644
index 00000000..26f43b6c
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionEvaluatorRegistry.java
@@ -0,0 +1,28 @@
+package com.zt.plat.module.databus.framework.integration.gateway.expression;
+
+import com.zt.plat.module.databus.enums.gateway.ExpressionTypeEnum;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Registry maintaining expression evaluators per language.
+ */
+@Component
+@RequiredArgsConstructor
+public class ExpressionEvaluatorRegistry {
+
+ private final Map evaluators = new EnumMap<>(ExpressionTypeEnum.class);
+
+ public void register(ExpressionTypeEnum type, ExpressionEvaluator evaluator) {
+ evaluators.put(type, evaluator);
+ }
+
+ public Optional lookup(ExpressionTypeEnum type) {
+ return Optional.ofNullable(evaluators.get(type));
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionExecutor.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionExecutor.java
new file mode 100644
index 00000000..4fb68902
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/expression/ExpressionExecutor.java
@@ -0,0 +1,51 @@
+package com.zt.plat.module.databus.framework.integration.gateway.expression;
+
+import com.zt.plat.framework.common.exception.ServiceException;
+import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
+import com.zt.plat.module.databus.enums.gateway.ExpressionTypeEnum;
+import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.Optional;
+
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_EXPRESSION_EVALUATION_FAILED;
+import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_EXPRESSION_NO_EVALUATOR;
+
+/**
+ * Executes expressions using registered evaluators.
+ */
+@Component
+@RequiredArgsConstructor
+public class ExpressionExecutor {
+
+ private final ExpressionEvaluatorRegistry registry;
+
+ public Object evaluate(ExpressionSpec spec, ApiInvocationContext invocation, Object payload, Map headers) throws Exception {
+ if (spec == null || spec.getExpression() == null) {
+ return null;
+ }
+ ExpressionTypeEnum type = spec.getType();
+ return registry.lookup(type)
+ .orElseThrow(() -> ServiceExceptionUtil.exception(API_EXPRESSION_NO_EVALUATOR, type == null ? "" : type.name()))
+ .evaluate(spec.getExpression(), ExpressionEvaluationContext.builder()
+ .invocation(invocation)
+ .payload(payload)
+ .variables(invocation.getVariables())
+ .headers(headers)
+ .build());
+ }
+
+ public Optional