mateclaw/mateclaw-server/src/main/java/vip/mate/MateClawApplication.java
铭萱 446f34b6b5
feat(db): support KingbaseES (人大金仓) domestic database (#324)
Add KingbaseES support as an opt-in profile: dedicated migration tree, bilingual seed data, runtime DbType detection (KINGBASE_ES / POSTGRE_SQL), and JDBC URL handling in the datasource manager.
2026-06-14 10:30:09 +08:00

123 lines
6.0 KiB
Java

package vip.mate;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* MateClaw - Personal AI Assistant
* Powered by Spring AI Alibaba
*
* @author MateClaw Team
*/
@Slf4j
@SpringBootApplication(exclude = {
// Disable Spring AI MCP Client auto-configuration (lifecycle owned by McpClientManager).
org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration.class,
org.springframework.ai.mcp.client.common.autoconfigure.annotations.McpClientAnnotationScannerAutoConfiguration.class,
org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration.class,
org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration.class,
// DashScopeAgent is the Bailian "Application Agent" (Bailian-hosted prompt+tool app),
// not the chat model. We don't use it — model configuration is admin-UI driven and
// built by DashScopeChatModelBuilder. Its auto-config strictly requires
// spring.ai.dashscope.api-key to be non-empty at startup, which makes the whole
// ApplicationContext fail when users deploy via Docker without setting the key.
com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration.class,
})
@EnableScheduling
@MapperScan("vip.mate.**.repository")
public class MateClawApplication {
@Autowired
private DataSource dataSource;
/** Cached DbType for the PaginationInnerInterceptor. */
private volatile DbType resolvedDbType;
public static void main(String[] args) {
SpringApplication.run(MateClawApplication.class, args);
}
/**
* Detect the actual database type from the live DataSource so the
* {@link PaginationInnerInterceptor} always uses the correct dialect,
* even when the JDBC URL is wrapped by a proxy (HikariCP, P6Spy, etc.).
*
* <p>DbType is cached after the first successful detection; a failure
* falls back to the value set in {@code mybatis-plus.global-config.db-config.db-type},
* or eventually to {@link DbType#MYSQL} — but by then the connection
* pool would already have failed.
*/
@PostConstruct
void detectDbType() {
try (Connection conn = dataSource.getConnection()) {
String productName = conn.getMetaData().getDatabaseProductName().toLowerCase();
if (productName.contains("kingbase")) {
resolvedDbType = DbType.KINGBASE_ES;
} else if (productName.contains("postgresql")) {
resolvedDbType = DbType.POSTGRE_SQL;
} else if (productName.contains("mysql") || productName.contains("mariadb")) {
resolvedDbType = DbType.MYSQL;
} else if (productName.contains("h2")) {
resolvedDbType = DbType.H2;
} else {
// Let the PaginationInnerInterceptor auto-detect at query time
resolvedDbType = null;
}
if (resolvedDbType != null) {
log.info("Detected database type: {} (product={})", resolvedDbType, productName);
}
} catch (Exception e) {
log.warn("Could not detect database type — PaginationInnerInterceptor will auto-detect on first query: {}",
e.getMessage());
}
}
/**
* MyBatis Plus pagination plugin.
*
* <p>When {@code resolvedDbType} is available the interceptor uses it directly;
* otherwise it falls back to JDBC-URL auto-detection, which works for
* {@code jdbc:kingbase8://} but not for proxied DataSources (RFC-042 P0).
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor pagination = resolvedDbType != null
? new PaginationInnerInterceptor(resolvedDbType)
: new PaginationInnerInterceptor();
interceptor.addInnerInterceptor(pagination);
return interceptor;
}
/**
* Print a clear "READY" banner after all post-startup initialization,
* so operators can tell at a glance when the application is ready to serve.
*/
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
log.info("");
log.info("╔══════════════════════════════════════════════════════════════════════╗");
log.info("║ MateClaw is READY ✓ ║");
log.info("║ Web UI → http://localhost:18088 ║");
log.info("║ Swagger → http://localhost:18088/swagger-ui.html ║");
log.info("╚══════════════════════════════════════════════════════════════════════╝");
log.info("");
}
}