10 Commits

Author SHA1 Message Date
v-zhangjc9
8fbc665abf feat(ai): 增加ai相关子项目 2025-05-15 17:15:18 +08:00
v-zhangjc9
a129caf5f4 fix(web): 修复思考开关无效 2025-05-14 09:14:35 +08:00
v-zhangjc9
aea8a7ed59 feat(web): 优化对话界面显示 2025-05-13 16:13:07 +08:00
v-zhangjc9
dd2e56e27b feat(web): 增加AI对话的能力 2025-05-13 16:03:08 +08:00
v-zhangjc9
819d56fbe3 feat(web): 优化图表展示 2025-05-13 12:06:21 +08:00
v-zhangjc9
255aad4987 feat(web): 优化图标显示 2025-05-12 19:28:23 +08:00
v-zhangjc9
f23de7c959 fix(web): 修复页面显示不正确、logo显示异常 2025-05-12 18:15:10 +08:00
v-zhangjc9
b0603d10bc feat(web): 优化页面跳转和菜单展现 2025-05-12 15:59:46 +08:00
v-zhangjc9
1e7b195f9f feat(web): 更换页面框架为pro-layout 2025-05-12 10:42:59 +08:00
v-zhangjc9
aa93b52dd9 refactor(web): 优化部署打包方案 2025-05-09 17:23:26 +08:00
55 changed files with 2880 additions and 765 deletions

84
service-ai/.gitignore vendored Normal file
View File

@@ -0,0 +1,84 @@
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/**/aws.xml
.idea/**/contentModel.xml
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
.idea/**/gradle.xml
.idea/**/libraries
cmake-build-*/
.idea/**/mongoSettings.xml
*.iws
out/
.idea_modules/
atlassian-ide-plugin.xml
.idea/replstate.xml
.idea/sonarlint/
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
.idea/httpRequests
.idea/caches/build_file_checksums.ser
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.project
.classpath
*~
.fuse_hidden*
.directory
.Trash-*
.nfs*
.gradle
**/build/
!src/**/build/
gradle-app.setting
!gradle-wrapper.jar
!gradle-wrapper.properties
.gradletasknamecache
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msix
*.msm
*.msp
*.lnk
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

10
service-ai/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 依赖于环境的 Maven 主目录路径
/mavenHomeManager.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ApifoxUploaderProjectSetting">
<option name="apiAccessToken" value="APS-0ZZaS4q0gUiFOlbBJMN8hAmS7viQNi4D" />
</component>
</project>

29
service-ai/.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

15
service-ai/.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="service-ai-knowledge" />
<module name="service-ai-chat" />
<module name="service-ai-core" />
</profile>
</annotationProcessing>
</component>
</project>

13
service-ai/.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/service-ai-chat/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/service-ai-chat/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/service-ai-core/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/service-ai-core/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/service-ai-knowledge/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/service-ai-knowledge/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

30
service-ai/.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="lanyuanxiaoyao-maven-central" />
<option name="name" value="lanyuanxiaoyao-maven-central" />
<option name="url" value="https://maven.lanyuanxiaoyao.com/central" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.lanyuanxiaoyao.com/central" />
</remote-repository>
</component>
</project>

12
service-ai/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK" />
</project>

6
service-ai/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

172
service-ai/pom.xml Normal file
View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<description>Hudi AI服务集合</description>
<modules>
<module>service-ai-core</module>
<module>service-ai-chat</module>
<module>service-ai-knowledge</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build-tag>b2b12</build-tag>
<spring-boot.version>3.4.3</spring-boot.version>
<spring-cloud.version>2024.0.1</spring-cloud.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
<eclipse-collections.version>11.1.0</eclipse-collections.version>
<curator.version>5.1.0</curator.version>
<hutool.version>5.8.27</hutool.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 当前项目依赖 -->
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-configuration</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-dependencies</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-forest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- spring boot 相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot3-starter</artifactId>
<version>1.5.36</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-eclipse-collections</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!-- 日志相关 -->
<dependency>
<groupId>pl.tkowalcz.tjahzi</groupId>
<artifactId>logback-appender</artifactId>
<version>0.9.23</version>
</dependency>
<dependency>
<groupId>com.github.loki4j</groupId>
<artifactId>loki-logback-appender-jdk8</artifactId>
<version>1.4.2</version>
</dependency>
<!-- 其他 -->
<dependency>
<groupId>dev.failsafe</groupId>
<artifactId>failsafe</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>${eclipse-collections.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections-api</artifactId>
<version>${eclipse-collections.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<distributionManagement>
<repository>
<id>${releases.id}</id>
<name>${releases.name}</name>
<url>${releases.url}</url>
</repository>
<snapshotRepository>
<id>${snapshots.id}</id>
<name>${snapshots.name}</name>
<url>${snapshots.url}</url>
</snapshotRepository>
</distributionManagement>
</project>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>service-ai-chat</artifactId>
<dependencies>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,23 @@
package com.lanyuanxiaoyao.service.ai.chat;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.retry.annotation.EnableRetry;
/**
* @author lanyuanxiaoyao
* @version 20250514
*/
@SpringBootApplication(scanBasePackages = "com.lanyuanxiaoyao.service")
@EnableDiscoveryClient
@EnableConfigurationProperties
@EnableEncryptableProperties
@EnableRetry
public class AiChatApplication {
public static void main(String[] args) {
SpringApplication.run(AiChatApplication.class, args);
}
}

View File

@@ -0,0 +1,65 @@
package com.lanyuanxiaoyao.service.ai.chat.controller;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* 聊天
*
* @author lanyuanxiaoyao
* @version 20250514
*/
@Controller
@RequestMapping("chat")
public class ChatController {
private static final Logger logger = LoggerFactory.getLogger(ChatController.class);
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
private ChatClient.ChatClientRequestSpec buildRequest(String message) {
return chatClient.prompt()
.user(message);
}
@PostMapping(value = "sync", consumes = "text/plain;charset=utf-8")
@ResponseBody
public String chatSync(@RequestBody String message) {
return buildRequest(message)
.call()
.content();
}
@PostMapping(value = "async", consumes = "text/plain;charset=utf-8")
public SseEmitter chatAsync(@RequestBody String message) {
SseEmitter emitter = new SseEmitter();
buildRequest(message)
.stream()
.content()
.subscribe(
content -> {
try {
emitter.send(content);
} catch (IOException e) {
emitter.completeWithError(e);
throw new RuntimeException(e);
}
},
emitter::completeWithError,
emitter::complete
);
return emitter;
}
}

View File

@@ -0,0 +1,36 @@
spring:
application:
name: service-ai-chat
profiles:
include: common,metrics,forest
cloud:
zookeeper:
enabled: true
connect-string: b1m2.hdp.dc:2181,b1m3.hdp.dc:2181,b1m4.hdp.dc:2181,b1m5.hdp.dc:2181,b1m6.hdp.dc:2181
discovery:
enabled: ${spring.cloud.zookeeper.enabled}
root: /hudi-services
instance-id: ${spring.application.name}-127.0.0.1-${random.uuid}-20250514
metadata:
discovery: zookeeper
ip: 127.0.0.1
hostname: localhost
hostname_full: localhost
start_time: 20250514112750
security:
meta:
authority: ENC(GXKnbq1LS11U2HaONspvH+D/TkIx13aWTaokdkzaF7HSvq6Z0Rv1+JUWFnYopVXu)
username: ENC(moIO5mO39V1Z+RDwROK9JXY4GfM8ZjDgM6Si7wRZ1MPVjbhTpmLz3lz28rAiw7c2LeCmizfJzHkEXIwGlB280g==)
darkcode: ENC(0jzpQ7T6S+P7bZrENgYsUoLhlqGvw7DA2MN3BRqEOwq7plhtg72vuuiPQNnr3DaYz0CpyTvxInhpx11W3VZ1trD6NINh7O3LN70ZqO5pWXk=)
ai:
openai:
base-url: http://132.121.206.65:10086
api-key: '*XMySqV%>hR&v>>g*NwCs3tpQ5FVMFEF2VHVTj<MYQd$&@$sY7CgqNyea4giJi4'
chat:
options:
model: 'Qwen3-1.7'
jasypt:
encryptor:
password: 'r#(R,P"Dp^A47>WSn:Wn].gs/+"v:q_Q*An~zF*g-@j@jtSTv5H/,S-3:R?r9R}.'
server:
port: 8080

View File

@@ -0,0 +1,34 @@
<configuration>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<springProperty scope="context" name="LOKI_PUSH_URL" source="loki.url"/>
<springProperty scope="context" name="LOGGING_PARENT" source="logging.parent"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5p) %clr([${HOSTNAME}]){yellow} %clr([%t]){magenta} %clr(%logger{40}){cyan} #@# %m%n%wEx</pattern>
</encoder>
</appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGGING_PARENT:-.}/${APP_NAME:-run}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOGGING_PARENT:-.}/archive/${APP_NAME:-run}-%d{yyyy-MM-dd}.gz</fileNamePattern>
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p [${HOSTNAME}] [%t] %logger #@# %m%n%wEx</pattern>
</encoder>
</appender>
<logger name="com.zaxxer.hikari" level="ERROR"/>
<logger name="com.netflix.discovery.shared.resolver.aws.ConfigClusterResolver" level="WARN"/>
<root level="INFO">
<appender-ref ref="Console"/>
<!-- <appender-ref ref="RollingFile"/>-->
</root>
</configuration>

View File

@@ -0,0 +1,37 @@
package com.lanyuanxiaoyao.service.ai.chat;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import reactor.core.Disposable;
/**
* @author lanyuanxiaoyao
* @version 20250514
*/
public class TestChat {
public static void main(String[] args) {
ChatClient client = ChatClient.builder(
OpenAiChatModel.builder()
.openAiApi(
OpenAiApi.builder()
.baseUrl("http://132.121.206.65:10086")
.apiKey("*XMySqV%>hR&v>>g*NwCs3tpQ5FVMFEF2VHVTj<MYQd$&@$sY7CgqNyea4giJi4")
.build()
)
.defaultOptions(
OpenAiChatOptions.builder()
.model("Qwen3-1.7")
.build()
)
.build()
)
.build();
String content = client.prompt()
.user("你好")
.call()
.content();
System.out.println(content);
}
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>service-ai-core</artifactId>
<dependencies>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-forest</artifactId>
<exclusions>
<exclusion>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot3-starter</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,58 @@
package com.lanyuanxiaoyao.service.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
/**
* @author lanyuanxiaoyao
* @version 20250514
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(registry -> registry.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.cors(configurer -> configurer.configurationSource(corsConfigurationSource()))
.formLogin(AbstractHttpConfigurer::disable)
.build();
}
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.addAllowedOriginPattern("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public InMemoryUserDetailsManager userDetailsService(SecurityProperties securityProperties) {
UserDetails user = User.builder()
.username(securityProperties.getUsername())
.password("{noop}" + securityProperties.getDarkcode())
.authorities(securityProperties.getAuthority())
.build();
return new InMemoryUserDetailsManager(user);
}
}

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>service-ai-knowledge</artifactId>
<dependencies>
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>service-ai-core</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,59 @@
package com.lanyuanxiaoyao.service.ai.knowledge;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import jakarta.annotation.Resource;
import java.net.MalformedURLException;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.model.Media;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.core.io.FileUrlResource;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.util.MimeTypeUtils;
/**
* @author lanyuanxiaoyao
* @version 20250515
*/
@SpringBootApplication(scanBasePackages = "com.lanyuanxiaoyao.service")
@EnableDiscoveryClient
@EnableConfigurationProperties
@EnableEncryptableProperties
@EnableRetry
public class KnowledgeApplication implements ApplicationRunner {
@Resource
private ChatClient.Builder builder;
public static void main(String[] args) {
SpringApplication.run(KnowledgeApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
ChatClient client = builder.build();
String content = client.prompt()
.user(
prompt -> {
try {
prompt
.text("如实描述图片中的内容,不要加入自己的思考以及与图片内容无关的任何文本")
.media(
Media.builder()
.mimeType(MimeTypeUtils.IMAGE_PNG)
.data(new FileUrlResource("/Users/lanyuanxiaoyao/Pictures/数据使用合同签订.png"))
.build()
);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
)
.call()
.content();
System.out.println(content);
}
}

View File

@@ -0,0 +1,24 @@
package com.lanyuanxiaoyao.service.ai.knowledge.controller;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lanyuanxiaoyao
* @version 20250515
*/
@RestController
@RequestMapping("knowledge")
public class KnowledgeController {
private static final Logger logger = LoggerFactory.getLogger(KnowledgeController.class);
public KnowledgeController() {
}
@PostConstruct
public void initial() {
}
}

View File

@@ -0,0 +1,43 @@
spring:
application:
name: service-ai-knowledge
profiles:
include: common,metrics,forest
cloud:
zookeeper:
enabled: true
connect-string: b1m2.hdp.dc:2181,b1m3.hdp.dc:2181,b1m4.hdp.dc:2181,b1m5.hdp.dc:2181,b1m6.hdp.dc:2181
discovery:
enabled: ${spring.cloud.zookeeper.enabled}
root: /hudi-services
instance-id: ${spring.application.name}-127.0.0.1-${random.uuid}-20250514
metadata:
discovery: zookeeper
ip: 127.0.0.1
hostname: localhost
hostname_full: localhost
start_time: 20250514112750
security:
meta:
authority: ENC(GXKnbq1LS11U2HaONspvH+D/TkIx13aWTaokdkzaF7HSvq6Z0Rv1+JUWFnYopVXu)
username: ENC(moIO5mO39V1Z+RDwROK9JXY4GfM8ZjDgM6Si7wRZ1MPVjbhTpmLz3lz28rAiw7c2LeCmizfJzHkEXIwGlB280g==)
darkcode: ENC(0jzpQ7T6S+P7bZrENgYsUoLhlqGvw7DA2MN3BRqEOwq7plhtg72vuuiPQNnr3DaYz0CpyTvxInhpx11W3VZ1trD6NINh7O3LN70ZqO5pWXk=)
ai:
openai:
base-url: http://localhost:3000
api-key: '*XMySqV%>hR&v>>g*NwCs3tpQ5FVMFEF2VHVTj<MYQd$&@$sY7CgqNyea4giJi4'
chat:
options:
model: '/models/InternVL3-1B-Instruct-Q8_0.gguf'
embedding:
options:
model: 'Bge-m3'
# vectorstore:
# qdrant:
# api-key: lanyuanxiaoyao
# initialize-schema: true
jasypt:
encryptor:
password: 'r#(R,P"Dp^A47>WSn:Wn].gs/+"v:q_Q*An~zF*g-@j@jtSTv5H/,S-3:R?r9R}.'
server:
port: 8080

View File

@@ -0,0 +1,34 @@
<configuration>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<springProperty scope="context" name="LOKI_PUSH_URL" source="loki.url"/>
<springProperty scope="context" name="LOGGING_PARENT" source="logging.parent"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5p) %clr([${HOSTNAME}]){yellow} %clr([%t]){magenta} %clr(%logger{40}){cyan} #@# %m%n%wEx</pattern>
</encoder>
</appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGGING_PARENT:-.}/${APP_NAME:-run}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOGGING_PARENT:-.}/archive/${APP_NAME:-run}-%d{yyyy-MM-dd}.gz</fileNamePattern>
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p [${HOSTNAME}] [%t] %logger #@# %m%n%wEx</pattern>
</encoder>
</appender>
<logger name="com.zaxxer.hikari" level="ERROR"/>
<logger name="com.netflix.discovery.shared.resolver.aws.ConfigClusterResolver" level="WARN"/>
<root level="INFO">
<appender-ref ref="Console"/>
<!-- <appender-ref ref="RollingFile"/>-->
</root>
</configuration>

View File

@@ -10,17 +10,22 @@
},
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@ant-design/pro-components": "^2.8.7",
"@ant-design/x": "^1.2.0",
"@fortawesome/fontawesome-free": "^6.7.2",
"@tinyflow-ai/react": "^0.1.6",
"amis": "^6.12.0",
"antd": "^5.25.0",
"axios": "^1.9.0",
"licia": "^1.48.0",
"markdown-it": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^7.5.3"
"react-router": "^7.5.3",
"styled-components": "^6.1.18"
},
"devDependencies": {
"@types/markdown-it": "^14.1.2",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react-swc": "^3.9.0",

View File

@@ -11,6 +11,12 @@ importers:
'@ant-design/icons':
specifier: ^6.0.0
version: 6.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-components':
specifier: ^2.8.7
version: 2.8.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/x':
specifier: ^1.2.0
version: 1.2.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@fortawesome/fontawesome-free':
specifier: ^6.7.2
version: 6.7.2
@@ -29,6 +35,9 @@ importers:
licia:
specifier: ^1.48.0
version: 1.48.0
markdown-it:
specifier: ^14.1.0
version: 14.1.0
react:
specifier: ^18.2.0
version: 18.3.1
@@ -38,7 +47,13 @@ importers:
react-router:
specifier: ^7.5.3
version: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
styled-components:
specifier: ^6.1.18
version: 6.1.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
'@types/markdown-it':
specifier: ^14.1.2
version: 14.1.2
'@types/react':
specifier: ^18.2.0
version: 18.3.21
@@ -113,21 +128,150 @@ packages:
react: '>=16.0.0'
react-dom: '>=16.0.0'
'@ant-design/pro-card@2.9.7':
resolution: {integrity: sha512-uDDYowmYH1ldRfG8Mb4QOwcEEz6ptRBQDLO1tkVADCRkdOMwz82xlZneR4uVuFyKcuNmgHzarYNncozBKhFuaA==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
'@ant-design/pro-components@2.8.7':
resolution: {integrity: sha512-QhibkPsUJryEjI1QmwUn+XCngGHidu0ekvricL6TIEvPgP+AUAca29XutN5+Mmn8Xfja1ca9HFTHTgFoV74Z7Q==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-descriptions@2.6.7':
resolution: {integrity: sha512-fgn2d0kDWUODGDWKpgziZuuqPlmIoKxQFJY9Yg4nbaRp8GDDKZeSSqgvW+OxjpYM8dxq31fiz1dZlZnOPoYKpg==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
'@ant-design/pro-field@3.0.4':
resolution: {integrity: sha512-nJSng/6/pPZFdiFeTtZcBQLNrHg9tIeiKFR1+zzbnQbI3qBOFP9aBZS/+LwkQZcI2G71vrRgz2x5OhHb7AX0wQ==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
'@ant-design/pro-form@2.31.7':
resolution: {integrity: sha512-0TCtIC/ynbLPoes8sLBFwFbi0tkeNmSU6the2EcyKIKDLfWHDbfkLM1OSFrzv3QD+H8OgFWMkTSOjhMOKSsOBg==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
rc-field-form: '>=1.22.0'
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-layout@7.22.4':
resolution: {integrity: sha512-X2WO4L2itXemX4zhS+0NG+8kXQD5SX9sG+zjx/15BmIO3FvsUGqOHgoCg0vhd424EiyPj7WtdMZJ39G1xdgDwA==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-list@2.6.7':
resolution: {integrity: sha512-6k/En7pioMgepho/1HMf2DAnkSTZiat1lDg2ggCok2lhSgqXzir7x22ewJQRgPvEiVb6/qqaFQNd7a8dnrFj1w==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-provider@2.15.4':
resolution: {integrity: sha512-DBX0JNUNOYXAucVqd/zTdqtXckCDqr2Lo85KIku2YzWdhptDPDZRTNqL04JShjGejDl8fzwQ8yREHgVUfzn6Gg==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-skeleton@2.2.1':
resolution: {integrity: sha512-3M2jNOZQZWEDR8pheY00OkHREfb0rquvFZLCa6DypGmiksiuuYuR9Y4iA82ZF+mva2FmpHekdwbje/GpbxqBeg==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-table@3.19.0':
resolution: {integrity: sha512-nL25734d5q5oqtmG7Apn2TNJUnJE8m9dkopXMQdoNZnv8qeRQLBH+i5cZT1yh7FIO8z6QLXleg+KnR/cI7VRRw==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
rc-field-form: '>=1.22.0'
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/pro-utils@2.17.0':
resolution: {integrity: sha512-hHKUISjMEoS+E5ltJWyvNTrlEA3IimZNxtDrEhorRIbgVYAlmEN5Mj/ESSofzDM3+UlxiI5+A/Y6IHkByTfDEA==}
peerDependencies:
antd: ^4.24.15 || ^5.11.2
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@ant-design/react-slick@1.1.2':
resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==}
peerDependencies:
react: '>=16.9.0'
'@ant-design/x@1.2.0':
resolution: {integrity: sha512-sV+C6ER+xn6XQF3gYN3Ma9MnK8Bcfj+5C4cRvtKNvzExHwC6laBD1g6TWHH7vFPDOQUhYlJT4tw+NdcvZJx/ZA==}
peerDependencies:
antd: ^5.20.3
react: '>=18.0.0'
react-dom: '>=18.0.0'
'@babel/runtime@7.27.1':
resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==}
engines: {node: '>=6.9.0'}
'@chenshuai2144/sketch-color@1.0.9':
resolution: {integrity: sha512-obzSy26cb7Pm7OprWyVpgMpIlrZpZ0B7vbrU0RMbvRg0YAI890S5Xy02Aj1Nhl4+KTbi1lVYHt6HQP8Hm9s+1w==}
peerDependencies:
react: '>=16.12.0'
'@ctrl/tinycolor@3.6.1':
resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
engines: {node: '>=10'}
'@dnd-kit/accessibility@3.1.1':
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
peerDependencies:
react: '>=16.8.0'
'@dnd-kit/core@6.3.1':
resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@dnd-kit/modifiers@6.0.1':
resolution: {integrity: sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==}
peerDependencies:
'@dnd-kit/core': ^6.0.6
react: '>=16.8.0'
'@dnd-kit/sortable@7.0.2':
resolution: {integrity: sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==}
peerDependencies:
'@dnd-kit/core': ^6.0.7
react: '>=16.8.0'
'@dnd-kit/utilities@3.2.2':
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
peerDependencies:
react: '>=16.8.0'
'@emotion/hash@0.8.0':
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
'@emotion/is-prop-valid@1.2.2':
resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==}
'@emotion/memoize@0.8.1':
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
'@emotion/unitless@0.7.5':
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
'@emotion/unitless@0.8.1':
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
'@esbuild/aix-ppc64@0.25.4':
resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
engines: {node: '>=18'}
@@ -788,6 +932,15 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
'@types/node@14.18.63':
resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==}
@@ -802,6 +955,9 @@ packages:
'@types/react@18.3.21':
resolution: {integrity: sha512-gXLBtmlcRJeT09/sI4PxVwyrku6SaNUj/6cMubjE6T6XdY1fDmBL7r0nX0jbSZPU/Xr0KuwLLZh6aOYY5d91Xw==}
'@types/stylis@4.2.5':
resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==}
'@types/warning@3.0.3':
resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==}
@@ -852,6 +1008,14 @@ packages:
resolution: {integrity: sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@umijs/route-utils@4.0.1':
resolution: {integrity: sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==}
'@umijs/use-params@1.0.9':
resolution: {integrity: sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==}
peerDependencies:
react: '*'
'@vitejs/plugin-react-swc@3.9.0':
resolution: {integrity: sha512-jYFUSXhwMCYsh/aQTgSGLIN3Foz5wMbH9ahb0Zva//UzwZYbMiZd7oT3AU9jHT9DLswYDswsRwPU9jVF3yA48Q==}
peerDependencies:
@@ -882,6 +1046,9 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
add-dom-event-listener@1.1.0:
resolution: {integrity: sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==}
adler-32@1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
engines: {node: '>=0.8'}
@@ -1054,6 +1221,9 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
canvas@2.11.2:
resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==}
engines: {node: '>=6'}
@@ -1186,6 +1356,13 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
css-color-keywords@1.0.0:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
css-to-react-native@3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -1312,6 +1489,10 @@ packages:
entities@2.1.0:
resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -1741,6 +1922,9 @@ packages:
linkify-it@3.0.3:
resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
listenercount@1.0.1:
resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
@@ -1834,6 +2018,10 @@ packages:
resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==}
hasBin: true
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
match-sorter@6.3.4:
resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==}
@@ -1847,6 +2035,9 @@ packages:
mdurl@1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
media-typer@1.1.0:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
@@ -2099,6 +2290,13 @@ packages:
resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==}
engines: {node: '>=16.20.0'}
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
postcss@8.4.49:
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
engines: {node: ^10 || ^12 || >=14}
@@ -2123,6 +2321,10 @@ packages:
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2297,6 +2499,12 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
rc-resize-observer@0.2.6:
resolution: {integrity: sha512-YX6nYnd6fk7zbuvT6oSDMKiZjyngjHoy+fz+vL3Tez38d/G5iGdaDJa2yE7345G6sc4Mm1IGRUIwclvltddhmA==}
peerDependencies:
react: '>=16.9.0'
react-dom: '>=16.9.0'
rc-resize-observer@1.4.3:
resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==}
peerDependencies:
@@ -2381,6 +2589,9 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
rc-util@4.21.1:
resolution: {integrity: sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==}
rc-util@5.44.4:
resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==}
peerDependencies:
@@ -2571,6 +2782,10 @@ packages:
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
@@ -2618,6 +2833,9 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shallowequal@1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -2690,6 +2908,16 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
styled-components@6.1.18:
resolution: {integrity: sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==}
engines: {node: '>= 16'}
peerDependencies:
react: '>= 16.8.0'
react-dom: '>= 16.8.0'
stylis@4.3.2:
resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==}
stylis@4.3.6:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
@@ -2701,6 +2929,11 @@ packages:
resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==}
engines: {node: '>=18'}
swr@2.3.3:
resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
@@ -2756,6 +2989,9 @@ packages:
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -2789,6 +3025,9 @@ packages:
uc.micro@1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
uncontrollable@7.2.1:
resolution: {integrity: sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==}
peerDependencies:
@@ -2831,6 +3070,11 @@ packages:
'@types/react':
optional: true
use-sync-external-store@1.5.0:
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -3019,6 +3263,194 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@ant-design/pro-card@2.9.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
transitivePeerDependencies:
- react-dom
'@ant-design/pro-components@2.8.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/pro-card': 2.9.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-descriptions': 2.6.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-field': 3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-form': 2.31.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-layout': 7.22.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-list': 2.6.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-skeleton': 2.2.1(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-table': 3.19.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
- rc-field-form
'@ant-design/pro-descriptions@2.6.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/pro-field': 3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-form': 2.31.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-skeleton': 2.2.1(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-resize-observer: 0.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
transitivePeerDependencies:
- rc-field-form
- react-dom
'@ant-design/pro-field@3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
'@chenshuai2144/sketch-color': 1.0.9(react@18.3.1)
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
dayjs: 1.11.13
lodash: 4.17.21
lodash-es: 4.17.21
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
swr: 2.3.3(react@18.3.1)
transitivePeerDependencies:
- react-dom
'@ant-design/pro-form@2.31.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-field': 3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
'@chenshuai2144/sketch-color': 1.0.9(react@18.3.1)
'@umijs/use-params': 1.0.9(react@18.3.1)
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
dayjs: 1.11.13
lodash: 4.17.21
lodash-es: 4.17.21
rc-field-form: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@ant-design/pro-layout@7.22.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
'@umijs/route-utils': 4.0.1
'@umijs/use-params': 1.0.9(react@18.3.1)
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
lodash: 4.17.21
lodash-es: 4.17.21
path-to-regexp: 8.2.0
rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
swr: 2.3.3(react@18.3.1)
warning: 4.0.3
'@ant-design/pro-list@2.6.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-card': 2.9.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-field': 3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-table': 3.19.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
dayjs: 1.11.13
rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 4.21.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
- rc-field-form
'@ant-design/pro-provider@2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
'@ctrl/tinycolor': 3.6.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
dayjs: 1.11.13
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
swr: 2.3.3(react@18.3.1)
'@ant-design/pro-skeleton@2.2.1(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@ant-design/pro-table@3.19.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-card': 2.9.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-field': 3.0.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-form': 2.31.7(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-utils': 2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
'@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/modifiers': 6.0.1(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
dayjs: 1.11.13
lodash: 4.17.21
lodash-es: 4.17.21
rc-field-form: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@ant-design/pro-utils@2.17.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/pro-provider': 2.15.4(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
dayjs: 1.11.13
lodash: 4.17.21
lodash-es: 4.17.21
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
safe-stable-stringify: 2.5.0
swr: 2.3.3(react@18.3.1)
'@ant-design/react-slick@1.1.2(react@18.3.1)':
dependencies:
'@babel/runtime': 7.27.1
@@ -3028,12 +3460,75 @@ snapshots:
resize-observer-polyfill: 1.5.1
throttle-debounce: 5.0.2
'@ant-design/x@1.2.0(antd@5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@ant-design/colors': 7.2.0
'@ant-design/cssinjs': 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@ant-design/fast-color': 2.0.6
'@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@babel/runtime': 7.27.1
antd: 5.25.0(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames: 2.5.1
rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@babel/runtime@7.27.1': {}
'@chenshuai2144/sketch-color@1.0.9(react@18.3.1)':
dependencies:
react: 18.3.1
reactcss: 1.2.3(react@18.3.1)
tinycolor2: 1.6.0
'@ctrl/tinycolor@3.6.1': {}
'@dnd-kit/accessibility@3.1.1(react@18.3.1)':
dependencies:
react: 18.3.1
tslib: 2.8.1
'@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/accessibility': 3.1.1(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
tslib: 2.8.1
'@dnd-kit/modifiers@6.0.1(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
tslib: 2.8.1
'@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
tslib: 2.8.1
'@dnd-kit/utilities@3.2.2(react@18.3.1)':
dependencies:
react: 18.3.1
tslib: 2.8.1
'@emotion/hash@0.8.0': {}
'@emotion/is-prop-valid@1.2.2':
dependencies:
'@emotion/memoize': 0.8.1
'@emotion/memoize@0.8.1': {}
'@emotion/unitless@0.7.5': {}
'@emotion/unitless@0.8.1': {}
'@esbuild/aix-ppc64@0.25.4':
optional: true
@@ -3565,6 +4060,15 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/linkify-it@5.0.0': {}
'@types/markdown-it@14.1.2':
dependencies:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
'@types/mdurl@2.0.0': {}
'@types/node@14.18.63': {}
'@types/prop-types@15.7.14': {}
@@ -3578,6 +4082,8 @@ snapshots:
'@types/prop-types': 15.7.14
csstype: 3.1.3
'@types/stylis@4.2.5': {}
'@types/warning@3.0.3': {}
'@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)':
@@ -3657,6 +4163,12 @@ snapshots:
'@typescript-eslint/types': 8.32.0
eslint-visitor-keys: 4.2.0
'@umijs/route-utils@4.0.1': {}
'@umijs/use-params@1.0.9(react@18.3.1)':
dependencies:
react: 18.3.1
'@vitejs/plugin-react-swc@3.9.0(vite@6.3.5(sass@1.87.0))':
dependencies:
'@swc/core': 1.11.24
@@ -3695,6 +4207,10 @@ snapshots:
acorn@8.14.1: {}
add-dom-event-listener@1.1.0:
dependencies:
object-assign: 4.1.1
adler-32@1.3.1: {}
agent-base@6.0.2:
@@ -4054,6 +4570,8 @@ snapshots:
callsites@3.1.0: {}
camelize@1.0.1: {}
canvas@2.11.2:
dependencies:
'@mapbox/node-pre-gyp': 1.0.11
@@ -4176,6 +4694,14 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
css-color-keywords@1.0.0: {}
css-to-react-native@3.2.0:
dependencies:
camelize: 1.0.1
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
csstype@3.1.3: {}
d3-color@3.1.0: {}
@@ -4290,6 +4816,8 @@ snapshots:
entities@2.1.0: {}
entities@4.5.0: {}
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
@@ -4804,6 +5332,10 @@ snapshots:
dependencies:
uc.micro: 1.0.6
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
listenercount@1.0.1: {}
locate-character@3.0.0: {}
@@ -4879,6 +5411,15 @@ snapshots:
mdurl: 1.0.1
uc.micro: 1.0.6
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
match-sorter@6.3.4:
dependencies:
'@babel/runtime': 7.27.1
@@ -4890,6 +5431,8 @@ snapshots:
mdurl@1.0.1: {}
mdurl@2.0.0: {}
media-typer@1.1.0: {}
merge-descriptors@2.0.0: {}
@@ -5100,6 +5643,14 @@ snapshots:
pkce-challenge@5.0.0: {}
postcss-value-parser@4.2.0: {}
postcss@8.4.49:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
postcss@8.5.3:
dependencies:
nanoid: 3.3.11
@@ -5127,6 +5678,8 @@ snapshots:
proxy-from-env@1.1.0: {}
punycode.js@2.3.1: {}
punycode@2.3.1: {}
pure-color@1.3.0: {}
@@ -5349,6 +5902,15 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
rc-resize-observer@0.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.27.1
classnames: 2.5.1
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
resize-observer-polyfill: 1.5.1
rc-resize-observer@1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.27.1
@@ -5473,6 +6035,14 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
rc-util@4.21.1:
dependencies:
add-dom-event-listener: 1.1.0
prop-types: 15.8.1
react-is: 16.13.1
react-lifecycles-compat: 3.0.4
shallowequal: 1.1.0
rc-util@5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.27.1
@@ -5729,6 +6299,8 @@ snapshots:
safe-buffer@5.2.1: {}
safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {}
sass@1.87.0:
@@ -5790,6 +6362,8 @@ snapshots:
setprototypeof@1.2.0: {}
shallowequal@1.1.0: {}
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -5873,6 +6447,22 @@ snapshots:
strip-json-comments@3.1.1: {}
styled-components@6.1.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@emotion/is-prop-valid': 1.2.2
'@emotion/unitless': 0.8.1
'@types/stylis': 4.2.5
css-to-react-native: 3.2.0
csstype: 3.1.3
postcss: 8.4.49
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
shallowequal: 1.1.0
stylis: 4.3.2
tslib: 2.6.2
stylis@4.3.2: {}
stylis@4.3.6: {}
supports-color@7.2.0:
@@ -5896,6 +6486,12 @@ snapshots:
magic-string: 0.30.17
zimmerframe: 1.1.2
swr@2.3.3(react@18.3.1):
dependencies:
dequal: 2.0.3
react: 18.3.1
use-sync-external-store: 1.5.0(react@18.3.1)
tar-stream@2.2.0:
dependencies:
bl: 4.1.0
@@ -5947,6 +6543,8 @@ snapshots:
tslib@2.3.0: {}
tslib@2.6.2: {}
tslib@2.8.1: {}
turbo-stream@2.4.0: {}
@@ -5977,6 +6575,8 @@ snapshots:
uc.micro@1.0.6: {}
uc.micro@2.1.0: {}
uncontrollable@7.2.1(react@18.3.1):
dependencies:
'@babel/runtime': 7.27.1
@@ -6023,6 +6623,10 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.21
use-sync-external-store@1.5.0(react@18.3.1):
dependencies:
react: 18.3.1
util-deprecate@1.0.2: {}
uuid@8.3.2: {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,25 +0,0 @@
import {Tinyflow} from '@tinyflow-ai/react'
import {type FormControlProps, FormItem} from 'amis'
import React from 'react'
import './Flow.scss'
import '@tinyflow-ai/react/dist/index.css'
const Flow: React.FC<FormControlProps> = props => {
const {onChange} = props
return (
<div className="flowable">
<Tinyflow
className="tinyflow-instance"
style={{height: '800px'}}
onDataChange={(value) => {
onChange(value)
}}
/>
</div>
)
}
FormItem({
type: 'flow',
autoVar: true,
})(Flow)

View File

@@ -1 +0,0 @@
import './Flow.tsx'

View File

@@ -1,75 +1,10 @@
import {createRoot} from 'react-dom/client'
import {createBrowserRouter, RouterProvider} from 'react-router'
import {createHashRouter, RouterProvider} from 'react-router'
import './components/Registry.ts'
import App from './pages/App.tsx'
import Cloud from './pages/Cloud.tsx'
import Conversation from './pages/Conversation.tsx'
import Home from './pages/Home.tsx'
import Overview from './pages/Overview.tsx'
import Queue from './pages/Queue.tsx'
import Table from './pages/Table.tsx'
import Task from './pages/Task.tsx'
import Tool from './pages/Tool.tsx'
import Version from './pages/Version.tsx'
import Yarn from './pages/Yarn.tsx'
import YarnCluster from './pages/YarnCluster.tsx'
const routes = createBrowserRouter([
{
path: '/',
Component: App,
children: [
{
Component: Home,
children: [
{
index: true,
Component: Overview,
},
{
path: '/home/table',
Component: Table,
},
{
path: '/home/queue',
Component: Queue,
},
{
path: '/home/version',
Component: Version,
},
{
path: '/home/yarn/:clusters/:queue/:search?',
Component: Yarn,
},
{
path: '/home/cloud',
Component: Cloud,
},
{
path: '/home/yarn_cluster',
Component: YarnCluster,
},
{
path: '/home/tool',
Component: Tool,
},
{
path: '/home/task',
Component: Task,
},
],
},
{
path: 'conversation',
Component: Conversation,
},
],
},
])
import {routes} from './route.tsx'
createRoot(document.getElementById('root')!).render(
<RouterProvider router={routes}/>,
<RouterProvider router={createHashRouter(routes)}/>,
)

View File

@@ -1,54 +1,41 @@
import {Layout, Menu} from 'antd'
import type {ItemType, MenuItemType} from 'antd/es/menu/interface'
import {contain, isEqual} from 'licia'
import React, {useEffect, useState} from 'react'
import {NavLink, Outlet, useLocation} from 'react-router'
const {Header, Content} = Layout
const headerNav: Array<MenuItemType> = [
{key: '/', label: '首页'},
{key: '/conversation', label: 'AI'},
]
import {ProLayout} from '@ant-design/pro-components'
import React, {useEffect} from 'react'
import {Outlet, useLocation, useNavigate} from 'react-router'
import {menus} from '../route.tsx'
const App: React.FC = () => {
const [_, setCurrentMenu] = useState<ItemType>()
const [selectedKeys, setSelectedKeys] = useState<Array<string>>([])
const navigate = useNavigate()
const location = useLocation()
useEffect(() => {
if (isEqual('/', location.pathname)) {
setSelectedKeys([location.pathname])
} else {
setSelectedKeys([headerNav.filter(nav => !isEqual(nav?.key, '/')).find(nav => contain(location.pathname, nav?.key))?.key as string ?? '/'])
}
setCurrentMenu(headerNav.find(nav => isEqual(nav?.key, location.pathname)))
}, [location])
return (
<Layout className="app h-full">
<Header className="header flex items-center p-5">
<div className="title font-sans text-white flex flex-col mr-10">
<div className="main-title text-xl font-extrabold leading-normal">Hudi </div>
<div className="sub-title text-gray-300 font-bold leading-normal">Hudi </div>
</div>
<Menu
className="header-nav"
theme="dark"
mode="horizontal"
selectedKeys={selectedKeys}
items={headerNav.map(nav => ({
key: nav.key,
label: <NavLink className="font-bold" to={nav.key as string}>{nav.label}</NavLink>,
}))}
style={{minWidth: 0, flex: 'auto'}}
/>
</Header>
<Layout>
<Content className="content">
<Outlet/>
</Content>
</Layout>
</Layout>
<ProLayout
token={{
header: {
colorBgHeader: '#292f33',
colorHeaderTitle: '#ffffff',
colorTextMenu: '#dfdfdf',
colorTextMenuSecondary: '#dfdfdf',
colorTextMenuSelected: '#ffffff',
colorTextMenuActive: '#ffffff',
colorBgMenuItemSelected: '#22272b',
colorTextRightActionsItem: '#dfdfdf',
},
}}
logo={<img src="icon.png" alt="logo"/>}
title="Hudi 服务总台"
route={menus}
location={{pathname: location.pathname}}
menu={{type: 'sub'}}
menuItemRender={(item, dom) => {
return <div onClick={() => navigate(item.path || '/')}>{dom}</div>
}}
fixSiderbar={true}
layout="mix"
splitMenus={true}
style={{minHeight: '100vh'}}
contentStyle={{backgroundColor: 'white', padding: '10px 10px 10px 20px'}}
>
<Outlet/>
</ProLayout>
)
}

View File

@@ -1,18 +0,0 @@
import {amisRender} from '../util/amis.ts'
function Conversation() {
return (
<div className="conversation">
{amisRender(
{
type: 'wrapper',
body: [
"逗你的,什么都没做,哎嘿!"
],
},
)}
</div>
)
}
export default Conversation

View File

@@ -1,83 +0,0 @@
import {
CheckSquareOutlined,
CloudOutlined,
ClusterOutlined,
CompressOutlined,
InfoCircleOutlined,
SunOutlined,
SyncOutlined,
TableOutlined,
ToolOutlined,
} from '@ant-design/icons'
import {Layout, Menu, theme} from 'antd'
import type {ItemType} from 'antd/es/menu/interface'
import {isNil} from 'licia'
import React from 'react'
import {NavLink, Outlet} from 'react-router'
import {commonInfo} from '../util/amis.ts'
const {Sider, Content} = Layout
const generateNavItem: any = (key: string, label: string, icon?: any) => {
let nav: any = {
key: key,
label: <NavLink to={key}>{label}</NavLink>,
}
if (!isNil(icon)) {
nav['icon'] = icon
}
return nav
}
const siderNav: ItemType[] = [
generateNavItem('/', '概览', <InfoCircleOutlined/>),
generateNavItem('/home/table', '表任务', <TableOutlined/>),
generateNavItem('/home/queue', '压缩队列', <CompressOutlined/>),
generateNavItem('/home/version', '跨天', <SunOutlined/>),
generateNavItem(`/home/yarn/${commonInfo.clusters.sync_names()}/root/Sync`, '同步集群', <SyncOutlined/>),
generateNavItem(
`/home/yarn/${commonInfo.clusters.compaction_names()}/default/Compaction`,
'压缩集群',
<SyncOutlined/>,
),
generateNavItem('/home/cloud', '服务', <CloudOutlined/>),
{
label: '集群',
icon: <ClusterOutlined/>,
children: [
generateNavItem('/home/yarn_cluster', '总览', <InfoCircleOutlined/>),
...Object.keys(commonInfo.clusters.compaction).map(name => generateNavItem(
// @ts-ignore
`/home/yarn/${name}/${commonInfo.clusters.compaction[name]}`,
`${name} 集群`,
<ClusterOutlined/>,
)),
],
},
generateNavItem('/home/tool', '工具', <ToolOutlined/>),
generateNavItem('/home/task', '任务', <CheckSquareOutlined/>),
]
const Home: React.FC = () => {
const {
token: {
colorBgContainer,
},
} = theme.useToken()
return (
<Layout className="home min-h-full">
<Sider width={170} style={{backgroundColor: colorBgContainer}}>
<Menu
mode="inline"
defaultSelectedKeys={['/']}
items={siderNav}
/>
</Sider>
<Content className="p-3">
<Outlet/>
</Content>
</Layout>
)
}
export default Home

View File

@@ -1,456 +0,0 @@
import React from 'react'
import {amisRender, commonInfo, crudCommonOptions, readOnlyDialogOptions} from '../util/amis.ts'
const color = (number: number) => {
let color = 'text-success'
if (number > 30) {
color = 'text-primary'
}
if (number > 90) {
color = 'text-danger'
}
return color
}
const versionDetailDialog = (variable: string, target: string) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
actions: [],
size: 'md',
closeOnEsc: false,
closeOnOutside: false,
body: [
{
type: 'service',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/version_detail`,
data: {
target: `${target}`,
version: '${version}',
},
},
body: [
{
type: 'table',
source: '${items}',
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
],
},
}
}
const tableDetailDialog = (variable: string, targetList: any) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
size: 'md',
...readOnlyDialogOptions(),
body: [
{
type: 'table',
source: `\${${targetList}}`,
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
}
}
const overviewYarnJob = (cluster: string, search: string, queue: string | undefined, yarnQueue: string) => {
return {
className: 'font-mono',
type: 'service',
api: `${commonInfo.baseUrl}/overview/yarn-job?cluster=${cluster}&search=${search}`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
className: 'mr-1 font-bold',
tpl: `\${PADSTART('${cluster}', 3)}`,
},
queue === undefined ? {} : {
type: 'service',
className: 'inline ml-2',
api: `${commonInfo.baseUrl}/overview/queue?queue=compaction-queue-${cluster}`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '${PADSTART(size, 2)}',
},
],
},
' ',
{
type: 'service',
className: 'inline',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/yarn-cluster`,
data: {
cluster: cluster,
queue: yarnQueue,
},
// @ts-ignore
adaptor: function (payload, response) {
let rootUsed = (payload['data']['root']['usedCapacity'] * 100 / payload['data']['root']['capacity'])
let targetUsed = (payload['data']['target']['absoluteUsedCapacity'] * 100 / payload['data']['target']['absoluteMaxCapacity'])
return {
...payload,
data: {
...payload.data,
rootUsed: rootUsed,
rootUsedColor: color(rootUsed),
targetUsed: targetUsed,
targetUsedColor: color(targetUsed),
},
}
},
},
interval: 10000,
silentPolling: true,
body: [
'(',
{
type: 'tpl',
tpl: '<span class="font-bold ${rootUsedColor}">${PADSTART(ROUND(rootUsed), 3)}%</span>',
},
',',
{
type: 'tpl',
tpl: '<span class="font-bold ${targetUsedColor}">${PADSTART(ROUND(targetUsed), 3)}%</span>',
},
')',
],
},
'(',
{
type: 'tpl',
tpl: '<span class=\'font-bold text-cyan-300\'>${PADSTART(scheduling, 2)}</span>',
},
',',
{
type: 'tpl',
tpl: '<span class="font-bold text-success">${PADSTART(running, 3)}</span>',
},
')',
],
}
}
const Overview: React.FC = () => {
return (
<div className="hudi-overview bg-white">
{amisRender(
{
type: 'wrapper',
body: [
{
type: 'service',
// language=JavaScript
dataProvider: 'const timer = setInterval(() => {\n setData({date: new Date().toLocaleString()})\n}, 1000)\nreturn () => {\n clearInterval(timer)\n}',
body: [
'当前时间:',
{
type: 'tpl',
className: 'font-bold',
tpl: '${date}',
},
],
},
{type: 'divider'},
'<span class="italic text-gray-500 my-2">表数量 (重点表数量, 普通表数量)</span>',
{
type: 'service',
api: `${commonInfo.baseUrl}/overview`,
interval: 60000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '逻辑表:<span class="font-bold mr-1 font-mono">${PADSTART(table_count, 4)} (<span class="text-primary">${PADSTART(table_focus_count, 4)}</span>, ${PADSTART(table_count - table_focus_count, 4)})</span>',
},
'<br>',
{
type: 'tpl',
tpl: '湖底表:<span class="font-bold mr-1 font-mono">${PADSTART(hudi_count, 4)} (<span class="text-primary">${PADSTART(hudi_focus_count, 4)}</span>, ${PADSTART(hudi_count - hudi_focus_count, 4)})</span>',
},
'<br>',
{
type: 'tpl',
tpl: '嗨福表:<span class="font-bold mr-1 font-mono">${PADSTART(hive_count, 4)} (<span class="text-primary">${PADSTART(hive_focus_count, 4)}</span>, ${PADSTART(hive_count - hive_focus_count, 4)})</span>',
},
],
},
{type: 'divider'},
{
type: 'service',
api: `${commonInfo.baseUrl}/overview/sync_running_status`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '任务数<span class="font-bold m-2 font-mono">${totalJob}</span>',
},
{
className: 'mx-2',
type: 'tpl',
tpl: '运行中<span class="font-bold m-2 font-mono">${PADSTART(runningJob, 3)}</span>',
},
{
type: 'tpl',
tpl: '已停止<span class="font-bold m-2 font-mono text-danger">${PADSTART(unRunningJob, 3)}</span>',
},
tableDetailDialog('unRunningJob', 'unRunningJobList'),
'<br/>',
{
type: 'tpl',
tpl: '总表数<span class="font-bold m-2 font-mono">${totalTable}</span>',
},
{
className: 'mx-2',
type: 'tpl',
tpl: '运行中<span class="font-bold m-2 font-mono">${PADSTART(runningTable, 3)}</span>',
},
{
type: 'tpl',
tpl: '已停止<span class="font-bold m-2 font-mono text-danger">${PADSTART(unRunningTable, 3)}</span>',
},
tableDetailDialog('unRunningTable', 'unRunningTableList'),
],
},
{type: 'divider'},
'<span class="italic text-gray-500 my-2">集群 (集群总资源使用,队列资源使用)(调度中任务数,运行中任务数)</span>',
overviewYarnJob(commonInfo.clusters.sync_names(), 'Sync', undefined, 'default'),
{type: 'divider'},
{
className: 'my-2',
type: 'service',
api: `${commonInfo.baseUrl}/overview/queue?queue=compaction-queue-pre`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '预调度队列:<span class="font-bold">${size}</span>',
},
],
},
'<span class="italic text-gray-500 my-2">集群 压缩队列任务数(集群总资源使用,队列资源使用)(调度中任务数,运行中任务数)</span>',
// @ts-ignore
...Object.keys(commonInfo.clusters.compaction).map(name => overviewYarnJob(name, 'Compaction', `compaction-queue-${name}`, commonInfo.clusters.compaction[name])),
{type: 'divider'},
{
type: 'service',
api: `${commonInfo.baseUrl}/overview/version`,
interval: 10000,
silentPolling: true,
body: [
'版本:',
{
type: 'tpl',
className: 'font-bold',
tpl: '${version}',
},
'<br/>',
'<span class="italic text-gray-500 my-2">未接收, 未跨天</span>',
'<br/>',
'重点表:',
{
type: 'tpl',
tpl: '<span class="font-bold font-mono">${PADSTART(unReceive.focus, 3)}</span>',
},
versionDetailDialog('unReceive.focus', 'unReceive_focus'),
',',
{
type: 'tpl',
tpl: '<span class="font-bold font-mono">${PADSTART(unSchedule.focus, 3)}</span>',
},
versionDetailDialog('unSchedule.focus', 'unScheduled_focus'),
'<br/>',
'普通表:',
{
type: 'tpl',
tpl: '<span class="font-bold font-mono">${PADSTART(unReceive.normal, 3)}</span>',
},
versionDetailDialog('unReceive.normal', 'unReceive_normal'),
',',
{
type: 'tpl',
tpl: '<span class="font-bold font-mono">${PADSTART(unSchedule.normal, 3)}</span>',
},
versionDetailDialog('unSchedule.normal', 'unScheduled_normal'),
],
},
{type: 'divider'},
{
type: 'service',
api: `${commonInfo.baseUrl}/overview/schedule_jobs`,
interval: 60000,
silentPolling: true,
body: [
'调度策略',
{
type: 'each',
name: 'items',
items: {
type: 'tpl',
tpl: '<div class="font-mono"><span class="font-bold">${trigger}</span> <span class="text-gray-500">(${job})</span></div>',
},
},
],
},
{type: 'divider'},
{
type: 'crud',
title: '监控指标运行进度',
api: `${commonInfo.baseUrl}/overview/monitor_progress`,
...crudCommonOptions(),
interval: 2000,
loadDataOnce: true,
columns: [
{
name: 'name',
label: '名称',
width: 120,
},
{
name: 'running',
label: '状态',
type: 'mapping',
width: 50,
map: {
'true': '运行中',
'false': '未运行',
},
},
{
label: '进度',
type: 'progress',
value: '${ROUND(progress * 100)}',
map: 'bg-primary',
},
],
},
],
},
)}
</div>
)
}
export default Overview

View File

@@ -0,0 +1,184 @@
import {ClearOutlined, UserOutlined} from '@ant-design/icons'
import {Bubble, Sender, useXAgent, useXChat, Welcome} from '@ant-design/x'
import {Button, Divider, Flex, Switch, Tooltip, Typography} from 'antd'
import markdownIt from 'markdown-it'
import {useRef, useState} from 'react'
import styled from 'styled-components'
const md = markdownIt({html: true, breaks: true})
const ConversationDiv = styled.div`
height: calc(100vh - 76px);
display: flex;
flex-direction: column;
padding: 10px;
.conversation-welcome {
flex: 1;
width: 70%;
margin: 30px auto 30px;
}
.conversation-list {
flex: 1;
margin-bottom: 30px;
padding-left: 30px;
padding-right: 30px;
think {
color: gray;
display: block;
border-left: 3px solid;
padding-left: 5px;
margin-bottom: 10px;
//white-space: pre-line;
}
}
.conversation-sender {
height: 100px;
padding-left: 30px;
padding-right: 30px;
}
`
const llmConfig = {
base: 'http://132.121.206.65:10086',
model: 'Qwen3-1.7',
secret: 'Bearer *XMySqV%>hR&v>>g*NwCs3tpQ5FVMFEF2VHVTj<MYQd$&@$sY7CgqNyea4giJi4',
}
function Conversation() {
const abortController = useRef<AbortController | null>(null)
const [input, setInput] = useState<string>('')
const [think, setThink] = useState<boolean>(true)
const [agent] = useXAgent<{ role: string, content: string }>({
baseURL: `${llmConfig.base}/v1/chat/completions`,
model: llmConfig.model,
dangerouslyApiKey: llmConfig.secret,
})
const {onRequest, messages, setMessages} = useXChat({
agent,
transformMessage: ({originMessage, chunk}) => {
let text = ''
try {
if (chunk?.data && !chunk?.data.includes('DONE')) {
const message = JSON.parse(chunk?.data)
text = !message?.choices?.[0].delta?.content
? ''
: message?.choices?.[0].delta?.content
}
} catch (error) {
console.error(error)
}
return {
content: (originMessage?.content || '') + text,
role: 'assistant',
}
},
resolveAbortController: controller => {
abortController.current = controller
},
})
return (
<ConversationDiv>
{messages.length > 0
? (<Bubble.List
className="conversation-list"
roles={{
assistant: {
placement: 'start',
avatar: {
icon: <img src="icon.png" alt=""/>,
style: {
background: 'transparent',
},
},
messageRender: content => {
let split = content.split('</think>')
if (split.length > 1) {
content = `${split[0]}</think>${md.render(split[1])}`
}
return (
<Typography>
<div dangerouslySetInnerHTML={{__html: content}}/>
</Typography>
)
},
},
user: {
placement: 'end',
avatar: {
icon: <UserOutlined/>,
},
},
}}
items={messages.map(({id, message}) => ({
key: id,
...message,
}))}
/>)
: (<div className="conversation-welcome">
<Welcome
variant="borderless"
icon={<img src="icon.png" alt="icon"/>}
title="你好,我是基于大模型深度思考技术构建的 AI 运营助手"
description="我可以帮你查询、检索Hudi 服务的运行情况,分析、处理 Hudi 服务的运营故障,输出、解读 Hudi 系统整体运营报告"
/>
</div>)}
<div className="conversation-sender">
<Sender
loading={agent.isRequesting()}
value={input}
onChange={setInput}
onSubmit={message => {
onRequest({
message: {
role: 'user',
content: (!think && messages.length === 0) ? `/no_think ${message}` : message,
},
stream: true,
})
setInput('')
}}
onCancel={() => abortController.current?.abort()}
footer={({components}) => {
const {SendButton, LoadingButton} = components
return (
<Flex justify="space-between" align="center">
<Flex gap="small" align="center">
<Switch
size="small"
value={think}
onChange={setThink}
disabled={messages.length > 0}
/>
<Divider type="vertical"/>
<Tooltip title="清空对话">
<Button
icon={<ClearOutlined/>}
type="text"
size="small"
onClick={() => setMessages([])}
/>
</Tooltip>
</Flex>
<Flex align="center">
{agent.isRequesting() ? (
<LoadingButton type="default"/>
) : (
<SendButton type="primary" disabled={false}/>
)}
</Flex>
</Flex>
)
}}
actions={false}
/>
</div>
</ConversationDiv>
)
}
export default Conversation

View File

@@ -0,0 +1,9 @@
function Inspection() {
return (
<div className="inspection">
</div>
)
}
export default Inspection

View File

@@ -6,7 +6,7 @@ import {
serviceLogByAppName,
serviceLogByAppNameAndHost,
time,
} from '../util/amis.ts'
} from '../../util/amis.ts'
const cloudCrud = (title: string, path: string) => {
return {
@@ -79,7 +79,7 @@ const cloudCrud = (title: string, path: string) => {
const Cloud: React.FC = () => {
return (
<div className="hudi-cloud bg-white">
<div className="hudi-cloud">
{amisRender(
{
type: 'wrapper',

View File

@@ -0,0 +1,635 @@
import React from 'react'
import {amisRender, commonInfo, crudCommonOptions, readOnlyDialogOptions} from '../../util/amis.ts'
const color = (number: number) => {
let color = 'text-success'
if (number > 30) {
color = 'text-primary'
}
if (number > 90) {
color = 'text-danger'
}
return color
}
const versionDetailDialog = (variable: string, target: string) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
actions: [],
size: 'md',
closeOnEsc: false,
closeOnOutside: false,
body: [
{
type: 'service',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/version_detail`,
data: {
target: target,
version: '${version}',
},
},
body: [
{
type: 'table',
source: '${items}',
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
],
},
}
}
const tableDetailDialog = (variable: string, targetList: any) => {
return {
disabledOn: `${variable} === 0`,
type: 'action',
label: '详情',
level: 'link',
size: 'sm',
actionType: 'dialog',
dialog: {
title: '详情',
size: 'md',
...readOnlyDialogOptions(),
body: [
{
type: 'table',
source: `\${${targetList}}`,
affixHeader: false,
columns: [
{
label: 'Flink job id',
fixed: 'left',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '${id}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${id}',
tooltip: '复制 ID',
},
],
},
{
label: '别名',
type: 'wrapper',
fixed: 'left',
size: 'none',
className: 'nowrap',
body: [
{
type: 'tpl',
tpl: '${alias}',
},
{
type: 'action',
level: 'link',
label: '',
icon: 'fa fa-copy',
size: 'xs',
actionType: 'copy',
content: '${alias}',
tooltip: '复制别名',
},
],
},
],
},
],
},
}
}
const overviewYarnJob = (cluster: string, search: string, queue: string | undefined, yarnQueue: string) => {
return {
className: 'text-base leading-none',
type: 'table-view',
border: false,
padding: '0 10px 0 15px',
trs: [
{
tds: [
{
body: `${cluster}`,
},
{
padding: '0px',
body: queue === undefined ? {} : {
type: 'service',
api: `${commonInfo.baseUrl}/overview/queue?queue=${queue}`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '${size}',
},
],
},
width: 100,
align: 'center',
},
{
padding: '0px',
width: 200,
body: {
type: 'service',
api: {
method: 'get',
url: `${commonInfo.baseUrl}/overview/yarn-cluster`,
data: {
cluster: cluster,
queue: yarnQueue,
},
// @ts-ignore
adaptor: function (payload, response) {
let rootUsed = (payload['data']['root']['usedCapacity'] * 100 / payload['data']['root']['capacity'])
let targetUsed = (payload['data']['target']['absoluteUsedCapacity'] * 100 / payload['data']['target']['absoluteMaxCapacity'])
return {
...payload,
data: {
...payload.data,
rootUsed: rootUsed,
rootUsedColor: color(rootUsed),
targetUsed: targetUsed,
targetUsedColor: color(targetUsed),
},
}
},
},
interval: 10000,
silentPolling: true,
body: {
type: 'table-view',
border: false,
trs: [
{
tds: [
{
body: {
type: 'tpl',
tpl: '<span class="font-bold ${rootUsedColor}">${ROUND(rootUsed, 0)}%</span>',
},
width: 100,
align: 'center',
},
{
body: {
type: 'tpl',
tpl: '<span class="font-bold ${targetUsedColor}">${ROUND(targetUsed, 0)}%</span>',
},
width: 100,
align: 'center',
},
],
},
],
},
},
},
{
padding: '0px',
width: 200,
body: {
type: 'service',
api: {
url: `${commonInfo.baseUrl}/overview/yarn-job`,
data: {
cluster: cluster,
search: search,
},
},
interval: 10000,
silentPolling: true,
body: {
type: 'table-view',
border: false,
trs: [
{
tds: [
{
body: {
type: 'tpl',
tpl: '<span class=\'font-bold text-cyan-300\'>${scheduling}</span>',
},
width: 100,
align: 'center',
},
{
body: {
type: 'tpl',
tpl: '<span class="font-bold text-success">${running}</span>',
},
width: 100,
align: 'center',
},
],
},
],
},
},
},
],
},
],
}
}
const Overview: React.FC = () => {
return (
<div className="hudi-overview">
{amisRender(
{
type: 'wrapper',
body: [
{
type: 'service',
// language=JavaScript
dataProvider: 'const timer = setInterval(() => {\n setData({date: new Date().toLocaleString()})\n}, 1000)\nreturn () => {\n clearInterval(timer)\n}',
body: [
'当前时间:',
{
type: 'tpl',
className: 'font-bold',
tpl: '${date}',
},
],
},
{type: 'divider'},
{
type: 'crud',
title: '同步表数量',
api: `${commonInfo.baseUrl}/overview`,
...crudCommonOptions(),
interval: 60000,
columns: [
{
name: 'type',
label: '表类型',
},
{
name: 'total',
label: '总表数',
width: 100,
align: 'center',
},
{
name: 'focus',
label: '重点表',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
label: '普通表',
type: 'tpl',
tpl: '${total - focus}',
width: 100,
align: 'center',
},
],
},
{
type: 'crud',
title: '同步表数量',
api: `${commonInfo.baseUrl}/overview/sync_running_status`,
...crudCommonOptions(),
interval: 10000,
columns: [
{
name: 'type',
label: '类型',
},
{
name: 'total',
label: '任务数',
width: 100,
align: 'center',
},
{
name: 'running',
label: '运行中',
width: 100,
align: 'center',
},
{
name: 'stopped',
label: '已停止',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
tableDetailDialog('stopped', 'list'),
],
},
],
},
{
className: 'pl-2 my-5',
type: 'wrapper',
size: 'none',
body: {
type: 'tpl',
tpl: '同步集群资源用量情况',
},
},
{
type: 'table-view',
border: false,
trs: [
{
background: '#F9F9F9',
tds: [
{
bold: true,
body: '集群',
},
{
bold: true,
body: '集群资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '队列资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '调度中',
width: 100,
align: 'center',
},
{
bold: true,
body: '运行中',
width: 100,
align: 'center',
},
],
},
],
},
overviewYarnJob(commonInfo.clusters.sync_names(), 'Sync', undefined, 'default'),
{type: 'divider'},
{
className: 'pl-2 my-5',
type: 'wrapper',
size: 'none',
body: [
{
type: 'tpl',
tpl: '压缩集群资源用量情况',
},
{
className: 'mt-2',
type: 'service',
api: `${commonInfo.baseUrl}/overview/queue?queue=compaction-queue-pre`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'tpl',
tpl: '预调度队列:<span class="font-bold">${size}</span>',
},
],
},
],
},
{
type: 'table-view',
border: false,
bold: true,
trs: [
{
background: '#F9F9F9',
tds: [
{
bold: true,
body: '集群',
},
{
bold: true,
body: '队列',
width: 100,
align: 'center',
},
{
bold: true,
body: '集群资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '队列资源',
width: 100,
align: 'center',
},
{
bold: true,
body: '调度中',
width: 100,
align: 'center',
},
{
bold: true,
body: '运行中',
width: 100,
align: 'center',
},
],
},
],
},
// @ts-ignore
...Object.keys(commonInfo.clusters.compaction).map(name => overviewYarnJob(name, 'Compaction', `compaction-queue-${name}`, commonInfo.clusters.compaction[name])),
{type: 'divider'},
{
type: 'service',
api: `${commonInfo.baseUrl}/overview/version`,
interval: 10000,
silentPolling: true,
body: [
{
type: 'table',
title: '跨天情况 (${version})',
source: '${items}',
...crudCommonOptions(),
headerToolbar: [
'${version}',
],
columns: [
{
name: 'type',
label: '类型',
},
{
name: 'unReceive',
label: '未接收',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
versionDetailDialog('unReceive', 'unReceive_${key}'),
],
},
{
name: 'unScheduled',
label: '未跨天',
className: 'text-danger font-bold',
width: 100,
align: 'center',
},
{
type: 'operation',
label: '操作',
width: 100,
align: 'center',
buttons: [
versionDetailDialog('unScheduled', 'unScheduled_${key}'),
],
},
],
},
],
},
{
type: 'crud',
title: '调度策略',
api: `${commonInfo.baseUrl}/overview/schedule_jobs`,
...crudCommonOptions(),
interval: 60000,
loadDataOnce: true,
columns: [
{
name: 'job',
label: '策略描述',
},
{
name: 'trigger',
label: 'Cron表达式',
className: 'font-mono',
width: 250,
},
],
},
{
type: 'crud',
title: '监控指标运行进度',
api: `${commonInfo.baseUrl}/overview/monitor_progress`,
...crudCommonOptions(),
interval: 2000,
loadDataOnce: true,
columns: [
{
name: 'name',
label: '名称',
width: 150,
},
{
name: 'running',
label: '状态',
type: 'mapping',
width: 70,
map: {
'true': '运行中',
'false': '未运行',
},
},
{
label: '进度',
type: 'progress',
value: '${ROUND(progress * 100)}',
map: 'bg-primary',
},
],
},
],
},
)}
</div>
)
}
export default Overview

View File

@@ -6,13 +6,13 @@ import {
paginationCommonOptions,
time,
yarnQueueCrud,
} from '../util/amis.ts'
} from '../../util/amis.ts'
const queueCrud = (name: string) => {
return {
type: 'crud',
title: name,
api: `\${base}/queue/all?name=${name}`,
api: `${commonInfo.baseUrl}/queue/all?name=${name}`,
...crudCommonOptions(),
interval: 10000,
loadDataOnce: true,
@@ -79,7 +79,7 @@ const Queue = () => {
items.push(queueCrud(`compaction-queue-${name}`))
}
return (
<div className="hudi-queue bg-white overflow-y-scroll">
<div className="hudi-queue">
{amisRender(
{
type: 'wrapper',

View File

@@ -14,11 +14,11 @@ import {
tableMetaDialog,
tableRunningStateMapping,
timeAndFrom,
} from '../util/amis.ts'
} from '../../util/amis.ts'
function Table() {
return (
<div className="hudi-table bg-white">
<div className="hudi-table">
{amisRender(
{
type: 'wrapper',

View File

@@ -1,9 +1,9 @@
import React from 'react'
import {amisRender, commonInfo, paginationCommonOptions, serviceLogByAppName, yarnCrudColumns} from '../util/amis.ts'
import {amisRender, commonInfo, paginationCommonOptions, serviceLogByAppName, yarnCrudColumns} from '../../util/amis.ts'
const Task: React.FC = () => {
return (
<div className="hudi-task bg-white">
<div className="hudi-task">
{amisRender(
{
type: 'wrapper',

View File

@@ -10,11 +10,11 @@ import {
paginationCommonOptions,
readOnlyDialogOptions,
timelineColumns,
} from '../util/amis.ts'
} from '../../util/amis.ts'
const Tool: React.FC = () => {
return (
<div className="hudi-tool bg-white">
<div className="hudi-tool">
{amisRender(
{
type: 'wrapper',

View File

@@ -10,7 +10,7 @@ import {
paginationCommonOptions,
tableMetaDialog,
versionUpdateStateMapping,
} from '../util/amis.ts'
} from '../../util/amis.ts'
function Version() {
return (

View File

@@ -7,13 +7,13 @@ import {
paginationCommonOptions,
yarnCrudColumns,
yarnQueueCrud,
} from '../util/amis.ts'
} from '../../util/amis.ts'
const Yarn: React.FC = () => {
const {clusters, queue, search} = useParams()
const location = useLocation()
return (
<div key={location.key} className="hudi-yarn bg-white">
<div key={location.key} className="hudi-yarn">
{amisRender(
{
type: 'wrapper',

View File

@@ -1,9 +1,9 @@
import React from 'react'
import {amisRender, commonInfo, yarnQueueCrud} from '../util/amis.ts'
import {amisRender, commonInfo, yarnQueueCrud} from '../../util/amis.ts'
const YarnCluster: React.FC = () => {
return (
<div className="hudi-yarn-cluster bg-white">
<div className="hudi-yarn-cluster">
{amisRender(
{
type: 'wrapper',

View File

@@ -0,0 +1,185 @@
import {
CheckSquareOutlined,
CloudOutlined,
ClusterOutlined,
CompressOutlined,
InfoCircleOutlined,
OpenAIOutlined,
QuestionOutlined,
SunOutlined,
SyncOutlined,
TableOutlined,
ToolOutlined,
} from '@ant-design/icons'
import {Navigate, type RouteObject} from 'react-router'
import Conversation from './pages/ai/Conversation.tsx'
import Inspection from './pages/ai/Inspection.tsx'
import App from './pages/App.tsx'
import Cloud from './pages/overview/Cloud.tsx'
import Overview from './pages/overview/Overview.tsx'
import Queue from './pages/overview/Queue.tsx'
import Table from './pages/overview/Table.tsx'
import Task from './pages/overview/Task.tsx'
import Tool from './pages/overview/Tool.tsx'
import Version from './pages/overview/Version.tsx'
import Yarn from './pages/overview/Yarn.tsx'
import YarnCluster from './pages/overview/YarnCluster.tsx'
import {commonInfo} from './util/amis.ts'
export const routes: RouteObject[] = [
{
path: '/',
Component: App,
children: [
{
index: true,
element: <Navigate to="/overview" replace/>,
},
{
path: 'overview',
Component: Overview,
},
{
path: 'table',
Component: Table,
},
{
path: 'queue',
Component: Queue,
},
{
path: 'version',
Component: Version,
},
{
path: 'yarn/:clusters/:queue/:search?',
Component: Yarn,
},
{
path: 'cloud',
Component: Cloud,
},
{
path: 'yarn_cluster',
Component: YarnCluster,
},
{
path: 'tool',
Component: Tool,
},
{
path: 'task',
Component: Task,
},
{
path: 'ai',
children: [
{
index: true,
element: <Navigate to="/ai/conversation" replace/>,
},
{
path: 'inspection',
Component: Inspection,
},
{
path: 'conversation',
Component: Conversation,
},
],
},
],
},
]
export const menus = {
routes: [
{
path: '/',
name: '概览',
icon: <InfoCircleOutlined/>,
routes: [
{
path: '/overview',
name: '概览',
icon: <InfoCircleOutlined/>,
},
{
path: '/table',
name: '表任务',
icon: <TableOutlined/>,
},
{
path: '/queue',
name: '压缩队列',
icon: <CompressOutlined/>,
},
{
path: '/version',
name: '跨天',
icon: <SunOutlined/>,
},
{
path: `/yarn/${commonInfo.clusters.sync_names()}/root/Sync`,
name: '同步集群',
icon: <SyncOutlined/>,
},
{
path: `/yarn/${commonInfo.clusters.compaction_names()}/default/Compaction`,
name: '压缩集群',
icon: <SyncOutlined/>,
},
{
path: '/cloud',
name: '服务',
icon: <CloudOutlined/>,
},
{
path: 'a86f7c51-ae60-4ca4-8c4d-40b86b445a04',
name: '集群',
icon: <ClusterOutlined/>,
routes: [
{
path: '/yarn_cluster',
name: '概览',
icon: <InfoCircleOutlined/>,
},
...Object.keys(commonInfo.clusters.compaction).map(name => ({
// @ts-ignore
path: `/yarn/${name}/${commonInfo.clusters.compaction[name]}`,
name: `${name} 集群`,
icon: <ClusterOutlined/>,
})),
],
},
{
path: '/tool',
name: '工具',
icon: <ToolOutlined/>,
},
{
path: '/task',
name: '任务',
icon: <CheckSquareOutlined/>,
},
],
},
{
path: '/ai',
name: 'AI',
icon: <OpenAIOutlined/>,
routes: [
{
path: '/ai/conversation',
name: '智慧问答',
icon: <QuestionOutlined/>,
},
{
path: '/ai/inspection',
name: '智能巡检',
icon: <CheckSquareOutlined/>,
},
],
},
],
}

View File

@@ -1,7 +1,13 @@
import {defineConfig} from 'vite'
import react from '@vitejs/plugin-react-swc'
import {defineConfig, UserConfig} from 'vite'
// https://vite.dev/config/
export default defineConfig({
export default defineConfig(({mode}) => {
let config: UserConfig = {
plugins: [react()],
}
if (mode === 'production') {
config.base = '/hudi_services/service_web'
}
return config
})

View File

@@ -28,6 +28,29 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>copy-web-dist</id>
<phase>compile</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/client/dist</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>

View File

@@ -8,27 +8,26 @@ import com.lanyuanxiaoyao.service.configuration.ExecutorProvider;
import com.lanyuanxiaoyao.service.configuration.entity.info.JobIdAndAlias;
import com.lanyuanxiaoyao.service.configuration.entity.monitor.MetricsProgress;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnApplication;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnQueue;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnRootQueue;
import com.lanyuanxiaoyao.service.configuration.entity.zookeeper.ZookeeperNode;
import com.lanyuanxiaoyao.service.forest.service.InfoService;
import com.lanyuanxiaoyao.service.forest.service.MonitorService;
import com.lanyuanxiaoyao.service.forest.service.QueueService;
import com.lanyuanxiaoyao.service.forest.service.ScheduleService;
import com.lanyuanxiaoyao.service.forest.service.YarnService;
import com.lanyuanxiaoyao.service.forest.service.ZookeeperService;
import com.lanyuanxiaoyao.service.forest.service.*;
import com.lanyuanxiaoyao.service.web.controller.base.AmisCrudResponse;
import com.lanyuanxiaoyao.service.web.controller.base.AmisMapResponse;
import com.lanyuanxiaoyao.service.web.controller.base.AmisResponse;
import com.lanyuanxiaoyao.service.web.controller.base.BaseController;
import com.lanyuanxiaoyao.service.web.entity.JobIdAndAliasVO;
import com.lanyuanxiaoyao.service.web.entity.ScheduleTimeVO;
import com.lanyuanxiaoyao.service.web.entity.overview.TableCountVO;
import com.lanyuanxiaoyao.service.web.entity.overview.TaskCountVO;
import com.lanyuanxiaoyao.service.web.entity.overview.VersionCountVO;
import com.lanyuanxiaoyao.service.web.entity.overview.YarnCountVO;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Maps;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.slf4j.Logger;
@@ -67,7 +66,7 @@ public class OverviewController extends BaseController {
}
@GetMapping("")
public AmisMapResponse overview() throws ExecutionException, InterruptedException {
public AmisCrudResponse overview() throws ExecutionException, InterruptedException {
CompletableFuture<Long> tableCountFuture = CompletableFuture.supplyAsync(infoService::tableCount, ExecutorProvider.EXECUTORS);
CompletableFuture<Long> tableFocusCountFuture = CompletableFuture.supplyAsync(infoService::tableFocusCount, ExecutorProvider.EXECUTORS);
CompletableFuture<Long> hudiCountFuture = CompletableFuture.supplyAsync(infoService::hudiCount, ExecutorProvider.EXECUTORS);
@@ -82,13 +81,12 @@ public class OverviewController extends BaseController {
hiveCountFuture,
hiveFocusCountFuture
).get();
return AmisResponse.responseMapData()
.setData("table_count", tableCountFuture.get())
.setData("table_focus_count", tableFocusCountFuture.get())
.setData("hudi_count", hudiCountFuture.get())
.setData("hudi_focus_count", hudiFocusCountFuture.get())
.setData("hive_count", hiveCountFuture.get())
.setData("hive_focus_count", hiveFocusCountFuture.get());
ImmutableList<TableCountVO> list = Lists.immutable.of(
new TableCountVO("逻辑表", tableCountFuture.get(), tableFocusCountFuture.get()),
new TableCountVO("湖底表", hudiCountFuture.get(), hudiFocusCountFuture.get()),
new TableCountVO("嗨福表", hiveCountFuture.get(), hiveFocusCountFuture.get())
);
return AmisResponse.responseCrudData(list);
}
@GetMapping("yarn-job")
@@ -99,7 +97,6 @@ public class OverviewController extends BaseController {
.setData("name", cluster)
.setData("total", applications.size())
.setData("running", applications.count(app -> StrUtil.equals(app.getState(), "RUNNING")))
.setData("running", applications.count(app -> StrUtil.equals(app.getState(), "RUNNING")))
.setData("scheduling", applications.count(app -> StrUtil.equals(app.getState(), "ACCEPTED")))
.setData("failure", applications.count(app -> StrUtil.equals(app.getState(), "FAILED")));
}
@@ -131,10 +128,13 @@ public class OverviewController extends BaseController {
CompletableFuture<Long> unScheduledNormalTableCount = CompletableFuture.supplyAsync(() -> infoService.unScheduledNormalTableCount(version), ExecutorProvider.EXECUTORS);
CompletableFuture<Long> unScheduledFocusTableCount = CompletableFuture.supplyAsync(() -> infoService.unScheduledFocusTableCount(version), ExecutorProvider.EXECUTORS);
CompletableFuture.allOf(unReceiveNormalTableCount, unReceiveFocusCount, unScheduledNormalTableCount, unScheduledFocusTableCount).get();
ImmutableList<VersionCountVO> list = Lists.immutable.of(
new VersionCountVO("focus", "重点表", unReceiveFocusCount.get(), unScheduledFocusTableCount.get()),
new VersionCountVO("normal", "普通表", unReceiveNormalTableCount.get(), unScheduledNormalTableCount.get())
);
return AmisResponse.responseMapData()
.setData("version", version)
.setData("unReceive", Maps.immutable.of("normal", unReceiveNormalTableCount.get(), "focus", unReceiveFocusCount.get()))
.setData("unSchedule", Maps.immutable.of("normal", unScheduledNormalTableCount.get(), "focus", unScheduledFocusTableCount.get()));
.setData("items", list);
}
@GetMapping("version_detail")
@@ -175,7 +175,7 @@ public class OverviewController extends BaseController {
}
@GetMapping("sync_running_status")
public AmisMapResponse syncRunningStatus() {
public AmisCrudResponse syncRunningStatus() {
ImmutableList<String> locks = zookeeperService.getChildren(NameHelper.ZK_SYNC_RUNNING_LOCK_PATH).collect(ZookeeperNode::getPath);
MutableList<JobIdAndAlias> runningJob = Lists.mutable.<JobIdAndAlias>empty().asSynchronized();
MutableList<JobIdAndAlias> unRunningJob = Lists.mutable.<JobIdAndAlias>empty().asSynchronized();
@@ -203,17 +203,11 @@ public class OverviewController extends BaseController {
unRunningTable.add(ia);
}
});
return AmisResponse.responseMapData()
.setData("totalJob", ids.size())
.setData("runningJob", runningJob.size())
.setData("runningJobList", runningJob)
.setData("unRunningJob", unRunningJob.size())
.setData("unRunningJobList", unRunningJob)
.setData("totalTable", idAliases.size())
.setData("runningTable", runningTable.size())
.setData("runningTableList", runningTable)
.setData("unRunningTable", unRunningTable.size())
.setData("unRunningTableList", unRunningTable);
ImmutableList<TaskCountVO> list = Lists.immutable.of(
new TaskCountVO("同步任务数", ids.size(), runningJob.size(), unRunningJob.size(), unRunningJob.toImmutable()),
new TaskCountVO("同步表数", idAliases.size(), runningTable.size(), unRunningTable.size(), unRunningTable.toImmutable())
);
return AmisResponse.responseCrudData(list);
}
@GetMapping("monitor_progress")

View File

@@ -0,0 +1,38 @@
package com.lanyuanxiaoyao.service.web.entity.overview;
/**
* @author lanyuanxiaoyao
* @version 20250509
*/
public class TableCountVO {
private final String type;
private final Long total;
private final Long focus;
public TableCountVO(String type, Long total, Long focus) {
this.type = type;
this.total = total;
this.focus = focus;
}
public String getType() {
return type;
}
public Long getTotal() {
return total;
}
public Long getFocus() {
return focus;
}
@Override
public String toString() {
return "TableCountVO{" +
"type='" + type + '\'' +
", total=" + total +
", focus=" + focus +
'}';
}
}

View File

@@ -0,0 +1,55 @@
package com.lanyuanxiaoyao.service.web.entity.overview;
import com.lanyuanxiaoyao.service.configuration.entity.info.JobIdAndAlias;
import org.eclipse.collections.api.list.ImmutableList;
/**
* @author lanyuanxiaoyao
* @version 20250509
*/
public class TaskCountVO {
private final String type;
private final Integer total;
private final Integer running;
private final Integer stopped;
private final ImmutableList<JobIdAndAlias> list;
public TaskCountVO(String type, Integer total, Integer running, Integer stopped, ImmutableList<JobIdAndAlias> list) {
this.type = type;
this.total = total;
this.running = running;
this.stopped = stopped;
this.list = list;
}
public String getType() {
return type;
}
public Integer getTotal() {
return total;
}
public Integer getRunning() {
return running;
}
public Integer getStopped() {
return stopped;
}
public ImmutableList<JobIdAndAlias> getList() {
return list;
}
@Override
public String toString() {
return "TaskCountVO{" +
"type='" + type + '\'' +
", total=" + total +
", running=" + running +
", stopped=" + stopped +
", list=" + list +
'}';
}
}

View File

@@ -0,0 +1,45 @@
package com.lanyuanxiaoyao.service.web.entity.overview;
/**
* @author lanyuanxiaoyao
* @version 20250509
*/
public class VersionCountVO {
private final String key;
private final String type;
private final Long unReceive;
private final Long unScheduled;
public VersionCountVO(String key, String type, Long unReceive, Long unScheduled) {
this.key = key;
this.type = type;
this.unReceive = unReceive;
this.unScheduled = unScheduled;
}
public String getKey() {
return key;
}
public String getType() {
return type;
}
public Long getUnReceive() {
return unReceive;
}
public Long getUnScheduled() {
return unScheduled;
}
@Override
public String toString() {
return "VersionCountVO{" +
"key='" + key + '\'' +
", type='" + type + '\'' +
", unReceive=" + unReceive +
", unScheduled=" + unScheduled +
'}';
}
}

View File

@@ -0,0 +1,76 @@
package com.lanyuanxiaoyao.service.web.entity.overview;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnQueue;
import com.lanyuanxiaoyao.service.configuration.entity.yarn.YarnRootQueue;
/**
* @author lanyuanxiaoyao
* @version 20250509
*/
public class YarnCountVO {
private final String cluster;
private final Integer total;
private final Integer running;
private final Integer scheduling;
private final Integer failure;
private final YarnRootQueue root;
private final YarnQueue target;
private final Integer queue;
public YarnCountVO(String cluster, Integer total, Integer running, Integer scheduling, Integer failure, YarnRootQueue root, YarnQueue target, Integer queue) {
this.cluster = cluster;
this.total = total;
this.running = running;
this.scheduling = scheduling;
this.failure = failure;
this.root = root;
this.target = target;
this.queue = queue;
}
public String getCluster() {
return cluster;
}
public Integer getTotal() {
return total;
}
public Integer getRunning() {
return running;
}
public Integer getScheduling() {
return scheduling;
}
public Integer getFailure() {
return failure;
}
public YarnRootQueue getRoot() {
return root;
}
public YarnQueue getTarget() {
return target;
}
public Integer getQueue() {
return queue;
}
@Override
public String toString() {
return "YarnCountVO{" +
"cluster='" + cluster + '\'' +
", total=" + total +
", running=" + running +
", scheduling=" + scheduling +
", failure=" + failure +
", root=" + root +
", target=" + target +
", queue=" + queue +
'}';
}
}

View File

@@ -7,23 +7,6 @@
<springProperty scope="context" name="LOGGING_PARENT" source="logging.parent"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<appender name="Loki" class="com.github.loki4j.logback.Loki4jAppender">
<metricsEnabled>true</metricsEnabled>
<http class="com.github.loki4j.logback.ApacheHttpSender">
<url>${LOKI_PUSH_URL:-http://localhost/loki/api/v1/push}</url>
</http>
<format>
<label>
<pattern>app=${APP_NAME:-none},host=${HOSTNAME:-none},level=%level</pattern>
<readMarkers>true</readMarkers>
</label>
<message>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p [${HOSTNAME}] [%t] %logger #@# %m%n%wEx</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5p) %clr([${HOSTNAME}]){yellow} %clr([%t]){magenta} %clr(%logger{40}){cyan} #@# %m%n%wEx</pattern>
@@ -45,7 +28,6 @@
<logger name="com.netflix.discovery.shared.resolver.aws.ConfigClusterResolver" level="WARN"/>
<root level="INFO">
<appender-ref ref="Loki"/>
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile"/>
</root>