Merge branch 'dev' into test

This commit is contained in:
chenbowen
2025-11-03 14:21:51 +08:00
21 changed files with 624 additions and 11 deletions

View File

@@ -0,0 +1,15 @@
-- DM8 字典导入按钮权限脚本
-- 幂等处理:清理旧的导入权限按钮,再重新写入
DELETE FROM system_role_menu WHERE menu_id = 103001;
DELETE FROM system_menu WHERE id = 103001;
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 (
103001, '字典导入', 'system:dict:import', 3, 6, 105, '#', '#', '', NULL,
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'
);
-- 如需同步给指定角色,请手工向 system_role_menu 插入对应关系记录

View File

@@ -0,0 +1,44 @@
-- 工艺工序页面菜单与权限初始化脚本DM8
-- 默认挂载在基础数据目录parent_id = 6200如需调整请修改 parent_id。
DELETE FROM system_menu
WHERE id IN (6207, 620701, 620702, 620703, 620704, 620705);
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 (
6207, '工艺工序', 'base:processing-infomation-operation:query', 2, 70, 6200,
'processing-infomation-operation', 'ep:operation', 'base/processinginfomationoperation/index', 'ProcessingInfomationOperation',
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
(620701, '工艺工序查询', 'base:processing-infomation-operation:query', 3, 1, 6207,
'', '', '', '',
0, '1', '1', '1',
'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(620702, '工艺工序创建', 'base:processing-infomation-operation:create', 3, 2, 6207,
'', '', '', '',
0, '1', '1', '1',
'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(620703, '工艺工序更新', 'base:processing-infomation-operation:update', 3, 3, 6207,
'', '', '', '',
0, '1', '1', '1',
'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(620704, '工艺工序删除', 'base:processing-infomation-operation:delete', 3, 4, 6207,
'', '', '', '',
0, '1', '1', '1',
'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(620705, '工艺工序导出', 'base:processing-infomation-operation:export', 3, 5, 6207,
'', '', '', '',
0, '1', '1', '1',
'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');

View File

@@ -86,6 +86,8 @@
<netty.version>4.1.116.Final</netty.version> <netty.version>4.1.116.Final</netty.version>
<mqtt.version>1.2.5</mqtt.version> <mqtt.version>1.2.5</mqtt.version>
<pf4j-spring.version>0.9.0</pf4j-spring.version> <pf4j-spring.version>0.9.0</pf4j-spring.version>
<!-- 规则引擎 -->
<liteflow.version>2.15.1</liteflow.version>
<vertx.version>4.5.13</vertx.version> <vertx.version>4.5.13</vertx.version>
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<commons-io.version>2.17.0</commons-io.version> <commons-io.version>2.17.0</commons-io.version>
@@ -661,6 +663,13 @@
</exclusions> </exclusions>
</dependency> </dependency>
<!-- 规则引擎 -->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${liteflow.version}</version>
</dependency>
<!-- PF4J --> <!-- PF4J -->
<dependency> <dependency>
<groupId>org.pf4j</groupId> <groupId>org.pf4j</groupId>

View File

@@ -49,4 +49,11 @@ public class ExcelUtils {
.doReadAllSync(); .doReadAllSync();
} }
public static <T> List<T> read(MultipartFile file, Class<T> head, int sheetNo) throws IOException {
return EasyExcel.read(file.getInputStream(), head, null)
.autoCloseStream(false)
.sheet(sheetNo)
.doReadSync();
}
} }

View File

@@ -272,6 +272,13 @@ spring:
- Path=/admin-api/databus/** - Path=/admin-api/databus/**
filters: filters:
- RewritePath=/admin-api/databus/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs - RewritePath=/admin-api/databus/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
x-forwarded: x-forwarded:
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀 prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀

View File

@@ -10,12 +10,13 @@ import lombok.Getter;
@Getter @Getter
public enum ApiStatusEnum { public enum ApiStatusEnum {
DRAFT(0), DRAFT(0, "草稿"),
ONLINE(1), ONLINE(1, "已上线"),
OFFLINE(2), OFFLINE(2, "已下线"),
DEPRECATED(3); DEPRECATED(3, "已废弃");
private final int status; private final int status;
private final String label;
public static boolean isOnline(Integer status) { public static boolean isOnline(Integer status) {
return status != null && status == ONLINE.status; return status != null && status == ONLINE.status;
@@ -25,4 +26,21 @@ public enum ApiStatusEnum {
return status != null && status == DEPRECATED.status; return status != null && status == DEPRECATED.status;
} }
public static ApiStatusEnum fromStatus(Integer status) {
if (status == null) {
return null;
}
for (ApiStatusEnum value : values()) {
if (value.status == status) {
return value;
}
}
return null;
}
public static String labelOf(Integer status) {
ApiStatusEnum value = fromStatus(status);
return value != null ? value.label : "未知";
}
} }

View File

@@ -4,21 +4,46 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import java.time.Duration;
@Configuration @Configuration
public class GatewayWebClientConfiguration { public class GatewayWebClientConfiguration {
private final int maxInMemorySize; private final int maxInMemorySize;
private final long maxIdleTimeMillis;
private final long evictInBackgroundMillis;
private final ReactorClientHttpConnector httpConnector;
public GatewayWebClientConfiguration( public GatewayWebClientConfiguration(
@Value("${databus.gateway.web-client.max-in-memory-size:20971520}") int maxInMemorySize) { @Value("${databus.gateway.web-client.max-in-memory-size:20971520}") int maxInMemorySize,
@Value("${databus.gateway.web-client.max-idle-time:45000}") long maxIdleTimeMillis,
@Value("${databus.gateway.web-client.evict-in-background-interval:20000}") long evictInBackgroundMillis) {
this.maxInMemorySize = maxInMemorySize; this.maxInMemorySize = maxInMemorySize;
this.maxIdleTimeMillis = maxIdleTimeMillis > 0 ? maxIdleTimeMillis : 45000L;
this.evictInBackgroundMillis = Math.max(evictInBackgroundMillis, 0L);
this.httpConnector = buildConnector();
} }
@Bean @Bean
public WebClientCustomizer gatewayWebClientCustomizer() { public WebClientCustomizer gatewayWebClientCustomizer() {
return builder -> builder.codecs(configurer -> return builder -> builder
configurer.defaultCodecs().maxInMemorySize(maxInMemorySize)); .clientConnector(httpConnector)
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(maxInMemorySize));
}
private ReactorClientHttpConnector buildConnector() {
ConnectionProvider.Builder providerBuilder = ConnectionProvider.builder("databus-gateway")
.maxIdleTime(Duration.ofMillis(maxIdleTimeMillis));
if (evictInBackgroundMillis > 0) {
providerBuilder.evictInBackground(Duration.ofMillis(evictInBackgroundMillis));
}
ConnectionProvider provider = providerBuilder.build();
HttpClient httpClient = HttpClient.create(provider).compress(true);
return new ReactorClientHttpConnector(httpClient);
} }
} }

View File

@@ -1,6 +1,7 @@
package com.zt.plat.module.databus.framework.integration.gateway.core; package com.zt.plat.module.databus.framework.integration.gateway.core;
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil; 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.model.ApiInvocationContext; import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.core.MessagingTemplate;
@@ -36,6 +37,24 @@ public class ApiFlowDispatcher {
return (ApiInvocationContext) reply.getPayload(); return (ApiInvocationContext) reply.getPayload();
} }
public ApiInvocationContext dispatchWithAggregate(ApiDefinitionAggregate aggregate, ApiInvocationContext context) {
IntegrationFlowManager.DebugFlowHandle handle = integrationFlowManager.obtainDebugHandle(aggregate);
MessageChannel channel = handle.channel();
Message<ApiInvocationContext> message = MessageBuilder.withPayload(context)
.setHeader("apiCode", aggregate.getDefinition().getApiCode())
.setHeader("version", aggregate.getDefinition().getVersion())
.build();
try {
Message<?> reply = messagingTemplate.sendAndReceive(channel, message);
if (reply == null) {
throw ServiceExceptionUtil.exception(API_FLOW_NO_REPLY, aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
}
return (ApiInvocationContext) reply.getPayload();
} finally {
integrationFlowManager.releaseDebugHandle(handle);
}
}
private MessageChannel requireInputChannel(String apiCode, String version) { private MessageChannel requireInputChannel(String apiCode, String version) {
// 未命中时,进行一次兜底补偿查询 // 未命中时,进行一次兜底补偿查询
return integrationFlowManager.locateInputChannel(apiCode, version) return integrationFlowManager.locateInputChannel(apiCode, version)

View File

@@ -3,12 +3,15 @@ package com.zt.plat.module.databus.framework.integration.gateway.core;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.exception.ServiceException; import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO; import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO;
import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties; import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties;
import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse; 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.model.ApiInvocationContext;
import com.zt.plat.module.databus.framework.integration.gateway.security.GatewayJwtResolver; import com.zt.plat.module.databus.framework.integration.gateway.security.GatewayJwtResolver;
import com.zt.plat.module.databus.framework.integration.gateway.security.GatewaySecurityFilter; import com.zt.plat.module.databus.framework.integration.gateway.security.GatewaySecurityFilter;
import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
@@ -23,6 +26,9 @@ import java.lang.reflect.Array;
import java.net.URI; import java.net.URI;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import static com.zt.plat.module.databus.service.gateway.impl.GatewayServiceErrorCodeConstants.API_DEFINITION_NOT_FOUND;
/** /**
* Orchestrates API portal request mapping, dispatch and response building so that * Orchestrates API portal request mapping, dispatch and response building so that
@@ -40,6 +46,7 @@ public class ApiGatewayExecutionService {
private static final String HEADER_QUERY_STRING = org.springframework.integration.http.HttpHeaders.PREFIX + "queryString"; private static final String HEADER_QUERY_STRING = org.springframework.integration.http.HttpHeaders.PREFIX + "queryString";
private static final String HEADER_REMOTE_ADDRESS = org.springframework.integration.http.HttpHeaders.PREFIX + "remoteAddress"; private static final String HEADER_REMOTE_ADDRESS = org.springframework.integration.http.HttpHeaders.PREFIX + "remoteAddress";
private static final String LOCAL_DEBUG_REMOTE_ADDRESS = "127.0.0.1"; private static final String LOCAL_DEBUG_REMOTE_ADDRESS = "127.0.0.1";
private static final String ATTR_DEBUG_INVOKE = "gatewayDebugInvoke";
private final ApiGatewayRequestMapper requestMapper; private final ApiGatewayRequestMapper requestMapper;
private final ApiFlowDispatcher apiFlowDispatcher; private final ApiFlowDispatcher apiFlowDispatcher;
@@ -47,6 +54,7 @@ public class ApiGatewayExecutionService {
private final ApiGatewayProperties properties; private final ApiGatewayProperties properties;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final ApiGatewayAccessLogger accessLogger; private final ApiGatewayAccessLogger accessLogger;
private final ApiDefinitionService apiDefinitionService;
/** /**
* Maps a raw HTTP message (as provided by Spring Integration) into a context message. * Maps a raw HTTP message (as provided by Spring Integration) into a context message.
@@ -67,8 +75,16 @@ public class ApiGatewayExecutionService {
ApiInvocationContext context = message.getPayload(); ApiInvocationContext context = message.getPayload();
accessLogger.onRequest(context); accessLogger.onRequest(context);
ApiInvocationContext responseContext; ApiInvocationContext responseContext;
ApiDefinitionAggregate debugAggregate = null;
try { try {
if (Boolean.TRUE.equals(context.getAttributes().get(ATTR_DEBUG_INVOKE))) {
debugAggregate = resolveDebugAggregate(context);
}
if (debugAggregate != null) {
responseContext = apiFlowDispatcher.dispatchWithAggregate(debugAggregate, context);
} else {
responseContext = apiFlowDispatcher.dispatch(context.getApiCode(), context.getApiVersion(), context); responseContext = apiFlowDispatcher.dispatch(context.getApiCode(), context.getApiVersion(), context);
}
} catch (ServiceException ex) { } catch (ServiceException ex) {
errorProcessor.applyServiceException(context, ex); errorProcessor.applyServiceException(context, ex);
accessLogger.onException(context, ex); accessLogger.onException(context, ex);
@@ -113,10 +129,20 @@ public class ApiGatewayExecutionService {
ApiInvocationContext context = mappedMessage.getPayload(); ApiInvocationContext context = mappedMessage.getPayload();
// Ensure query parameters & headers from debug payload are reflected after mapping. // Ensure query parameters & headers from debug payload are reflected after mapping.
mergeDebugMetadata(context, reqVO); mergeDebugMetadata(context, reqVO);
context.getAttributes().put(ATTR_DEBUG_INVOKE, Boolean.TRUE);
ApiInvocationContext responseContext = dispatch(mappedMessage); ApiInvocationContext responseContext = dispatch(mappedMessage);
return buildResponseEntity(responseContext); return buildResponseEntity(responseContext);
} }
private ApiDefinitionAggregate resolveDebugAggregate(ApiInvocationContext context) {
Optional<ApiDefinitionAggregate> activeDefinition = apiDefinitionService.findByCodeAndVersion(context.getApiCode(), context.getApiVersion());
if (activeDefinition.isPresent()) {
return activeDefinition.get();
}
return apiDefinitionService.findByCodeAndVersionIncludingInactive(context.getApiCode(), context.getApiVersion())
.orElseThrow(() -> ServiceExceptionUtil.exception(API_DEFINITION_NOT_FOUND));
}
private Message<?> buildDebugMessage(ApiGatewayInvokeReqVO reqVO) { private Message<?> buildDebugMessage(ApiGatewayInvokeReqVO reqVO) {
Object payload = preparePayload(reqVO.getPayload()); Object payload = preparePayload(reqVO.getPayload());
MessageBuilder<Object> builder = MessageBuilder.withPayload(payload); MessageBuilder<Object> builder = MessageBuilder.withPayload(payload);

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@@ -63,6 +64,33 @@ public class IntegrationFlowManager {
return Optional.ofNullable(registration.getInputChannel()); return Optional.ofNullable(registration.getInputChannel());
} }
public DebugFlowHandle obtainDebugHandle(ApiDefinitionAggregate aggregate) {
String key = key(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
IntegrationFlowContext.IntegrationFlowRegistration existing = activeRegistrations.get(key);
if (existing != null) {
return new DebugFlowHandle(existing.getInputChannel(), existing.getId(), false);
}
ApiFlowRegistration registration = apiFlowAssembler.assemble(aggregate);
String debugId = registration.getFlowId() + "#debug#" + UUID.randomUUID();
IntegrationFlowContext.IntegrationFlowRegistration debugRegistration = integrationFlowContext.registration(registration.getFlow())
.id(debugId)
.register();
log.debug("[API-PORTAL] 临时注册调试流程 {} 对应 apiCode={} version={}", debugId, aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
return new DebugFlowHandle(debugRegistration.getInputChannel(), debugRegistration.getId(), true);
}
public void releaseDebugHandle(DebugFlowHandle handle) {
if (handle == null || !handle.temporary) {
return;
}
try {
integrationFlowContext.remove(handle.registrationId);
log.debug("[API-PORTAL] 已移除调试流程 {}", handle.registrationId);
} catch (Exception ex) {
log.warn("移除调试流程 {} 失败", handle.registrationId, ex);
}
}
private void registerFlow(ApiDefinitionAggregate aggregate) { private void registerFlow(ApiDefinitionAggregate aggregate) {
String key = key(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion()); String key = key(aggregate.getDefinition().getApiCode(), aggregate.getDefinition().getVersion());
deregisterByKey(key); deregisterByKey(key);
@@ -93,4 +121,24 @@ public class IntegrationFlowManager {
private String key(String apiCode, String version) { private String key(String apiCode, String version) {
return (apiCode + ":" + version).toLowerCase(); return (apiCode + ":" + version).toLowerCase();
} }
public static final class DebugFlowHandle {
private final MessageChannel channel;
private final String registrationId;
private final boolean temporary;
private DebugFlowHandle(MessageChannel channel, String registrationId, boolean temporary) {
this.channel = channel;
this.registrationId = registrationId;
this.temporary = temporary;
}
public MessageChannel channel() {
return channel;
}
public boolean temporary() {
return temporary;
}
}
} }

Some files were not shown because too many files have changed in this diff Show More