初始化

This commit is contained in:
Lee 2026-06-09 16:03:44 +08:00
commit 3b8b4eb1cc
100 changed files with 4138 additions and 0 deletions

10
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

18
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,18 @@
<?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="tracexd" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="tracexd" options="-parameters" />
</option>
</component>
</project>

6
.idea/encodings.xml generated Normal file
View File

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

View File

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<inspection_tool class="VulnerableLibrariesGlobal" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="VulnerableLibrariesLocal" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

20
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?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="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>
</component>
</project>

12
.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" project-jdk-name="26" project-jdk-type="JavaSDK" />
</project>

6
.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>

12
app-err.log Normal file
View File

@ -0,0 +1,12 @@
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
WARNING: Final field _cipher in class org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher has been mutated reflectively by class org.eclipse.sisu.bean.BeanPropertyField in unnamed module @780cb77 (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/org.eclipse.sisu.inject-0.9.0.M3.jar)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled

78
app-out.log Normal file
View File

@ -0,0 +1,78 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.tracexd:tracexd >-------------------------
[INFO] Building tracexd 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.4.1:clean (default-clean) @ tracexd ---
[INFO] Deleting C:\Work\tracexd\target
[INFO]
[INFO] >>> spring-boot:3.5.14:run (default-cli) > test-compile @ tracexd >>>
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ tracexd ---
[INFO] Copying 2 resources from src\main\resources to target\classes
[INFO] Copying 6 resources from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.14.1:compile (default-compile) @ tracexd ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 27 source files with javac [debug parameters release 26] to target\classes
[INFO] /C:/Work/tracexd/src/main/java/com/tracexd/controller/VoiceController.java: 某些输入文件使用了未经检查或不安全的操作。
[INFO] /C:/Work/tracexd/src/main/java/com/tracexd/controller/VoiceController.java: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ tracexd ---
[INFO] skip non existing resourceDirectory C:\Work\tracexd\src\test\resources
[INFO]
[INFO] --- compiler:3.14.1:testCompile (default-testCompile) @ tracexd ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 1 source file with javac [debug parameters release 26] to target\test-classes
[INFO]
[INFO] <<< spring-boot:3.5.14:run (default-cli) < test-compile @ tracexd <<<
[INFO]
[INFO]
[INFO] --- spring-boot:3.5.14:run (default-cli) @ tracexd ---
[INFO] Attaching agents: []
2026-06-09T14:10:37.892+08:00 INFO 14244 --- [ main] com.tracexd.TracexdApplication : Starting TracexdApplication using Java 26.0.1 with PID 14244 (C:\Work\tracexd\target\classes started by 16332 in C:\Work\tracexd)
2026-06-09T14:10:37.893+08:00 DEBUG 14244 --- [ main] com.tracexd.TracexdApplication : Running with Spring Boot v3.5.14, Spring v6.2.18
2026-06-09T14:10:37.894+08:00 INFO 14244 --- [ main] com.tracexd.TracexdApplication : The following 1 profile is active: "test"
2026-06-09T14:10:38.478+08:00 INFO 14244 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-06-09T14:10:38.478+08:00 INFO 14244 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.54]
2026-06-09T14:10:38.504+08:00 INFO 14244 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Get /198.18.0.1 network interface
Get network interface info: name:iftype53_32769 (Meta Tunnel)
Initialization Sequence datacenterId:0 workerId:31
2026-06-09T14:10:38.600+08:00 INFO 14244 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-06-09T14:10:38.699+08:00 INFO 14244 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:tracexd user=SA
2026-06-09T14:10:38.699+08:00 INFO 14244 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-06-09T14:10:39.114+08:00 INFO 14244 --- [ main] m.e.s.MybatisPlusApplicationContextAware : Register ApplicationContext instances org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@e383572
2026-06-09T14:10:39.150+08:00 WARN 14244 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'
2026-06-09T14:10:39.151+08:00 INFO 14244 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-06-09T14:10:39.152+08:00 INFO 14244 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2026-06-09T14:10:39.166+08:00 ERROR 14244 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.910 s
[INFO] Finished at: 2026-06-09T14:10:39+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.5.14:run (default-cli) on project tracexd: Process terminated with exit code: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

71
app-out3.log Normal file
View File

@ -0,0 +1,71 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.tracexd:tracexd >-------------------------
[INFO] Building tracexd 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot:3.5.14:run (default-cli) > test-compile @ tracexd >>>
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ tracexd ---
[INFO] Copying 2 resources from src\main\resources to target\classes
[INFO] Copying 6 resources from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.14.1:compile (default-compile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ tracexd ---
[INFO] skip non existing resourceDirectory C:\Work\tracexd\src\test\resources
[INFO]
[INFO] --- compiler:3.14.1:testCompile (default-testCompile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] <<< spring-boot:3.5.14:run (default-cli) < test-compile @ tracexd <<<
[INFO]
[INFO]
[INFO] --- spring-boot:3.5.14:run (default-cli) @ tracexd ---
[INFO] Attaching agents: []
2026-06-09T14:11:45.473+08:00 INFO 12060 --- [ main] com.tracexd.TracexdApplication : Starting TracexdApplication using Java 26.0.1 with PID 12060 (C:\Work\tracexd\target\classes started by 16332 in C:\Work\tracexd)
2026-06-09T14:11:45.474+08:00 DEBUG 12060 --- [ main] com.tracexd.TracexdApplication : Running with Spring Boot v3.5.14, Spring v6.2.18
2026-06-09T14:11:45.475+08:00 INFO 12060 --- [ main] com.tracexd.TracexdApplication : The following 1 profile is active: "test"
2026-06-09T14:11:46.059+08:00 INFO 12060 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-06-09T14:11:46.060+08:00 INFO 12060 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.54]
2026-06-09T14:11:46.087+08:00 INFO 12060 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Get /198.18.0.1 network interface
Get network interface info: name:iftype53_32769 (Meta Tunnel)
Initialization Sequence datacenterId:0 workerId:25
2026-06-09T14:11:46.186+08:00 INFO 12060 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-06-09T14:11:46.285+08:00 INFO 12060 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:tracexd user=SA
2026-06-09T14:11:46.286+08:00 INFO 12060 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-06-09T14:11:46.713+08:00 INFO 12060 --- [ main] m.e.s.MybatisPlusApplicationContextAware : Register ApplicationContext instances org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@e383572
2026-06-09T14:11:46.748+08:00 WARN 12060 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'
2026-06-09T14:11:46.749+08:00 INFO 12060 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-06-09T14:11:46.750+08:00 INFO 12060 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2026-06-09T14:11:46.764+08:00 ERROR 12060 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.975 s
[INFO] Finished at: 2026-06-09T14:11:47+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.5.14:run (default-cli) on project tracexd: Process terminated with exit code: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

168
build.log Normal file
View File

@ -0,0 +1,168 @@
mvn : WARNING: A restricted method in java.lang.System has been called
所在位置 行:4 字符: 1
+ mvn compile 2>&1 | Out-File "C:\Work\tracexd\build.log" -Encoding utf ...
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (WARNING: A rest...has been called:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file
:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/jansi-2.4.1
.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeA
tomicHelper (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.
9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$Un
safeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
WARNING: Final field _cipher in class org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher has been mutat
ed reflectively by class org.eclipse.sisu.bean.BeanPropertyField in unnamed module @11dc3715 (file:/C:/Users/16332/.m2/
wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/org.eclipse.sisu.inject-0.9.0.M3
.jar)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
[INFO] Scanning for projects...
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/activemq-bom/6.1.8/activemq-bom-6.1.8.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/artemis-bom/2.40.0/artemis-bom-2.40.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/assertj/assertj-bom/3.27.7/assertj-bom-3.27.7.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/reporter2/zipkin-reporter-bom/3.5.3/zipkin-reporter-bom-3.5.3.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/brave/brave-bom/6.1.0/brave-bom-6.1.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/cassandra/java-driver-bom/4.19.2/java-driver-bom-4.19.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/glassfish/jaxb/jaxb-bom/4.0.6/jaxb-bom-4.0.6.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/groovy/groovy-bom/4.0.31/groovy-bom-4.0.31.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-bom/15.2.6.Final/infinispan-bom-15.2.6.Final.pom
Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-bom/2.21.2/jackson-bom-2.21.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/glassfish/jersey/jersey-bom/3.1.11/jersey-bom-3.1.11.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/eclipse/jetty/ee10/jetty-ee10-bom/12.0.34/jetty-ee10-bom-12.0.34.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/eclipse/jetty/jetty-bom/12.0.34/jetty-bom-12.0.34.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/junit/junit-bom/5.12.2/junit-bom-5.12.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-bom/1.9.25/kotlin-bom-1.9.25.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-bom/1.8.1/kotlinx-coroutines-bom-1.8.1.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-bom/1.6.3/kotlinx-serialization-bom-1.6.3.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/logging/log4j/log4j-bom/2.24.3/log4j-bom-2.24.3.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/micrometer/micrometer-bom/1.15.11/micrometer-bom-1.15.11.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/mockito/mockito-bom/5.17.0/mockito-bom-5.17.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/mongodb/mongodb-driver-bom/5.5.2/mongodb-driver-bom-5.5.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/netty/netty-bom/4.1.132.Final/netty-bom-4.1.132.Final.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/opentelemetry/opentelemetry-bom/1.49.0/opentelemetry-bom-1.49.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/prometheus/prometheus-metrics-bom/1.3.10/prometheus-metrics-bom-1.3.10.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/prometheus/simpleclient_bom/0.16.0/simpleclient_bom-0.16.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/pulsar/pulsar-bom/4.0.9/pulsar-bom-4.0.9.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/pulsar/pulsar-client-reactive-bom/0.6.0/pulsar-client-reactive-bom-0.6.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/com/querydsl/querydsl-bom/5.1.0/querydsl-bom-5.1.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/projectreactor/reactor-bom/2024.0.17/reactor-bom-2024.0.17.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/rest-assured/rest-assured-bom/5.5.7/rest-assured-bom-5.5.7.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/rsocket/rsocket-bom/1.1.5/rsocket-bom-1.1.5.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/seleniumhq/selenium/selenium-bom/4.31.0/selenium-bom-4.31.0.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/amqp/spring-amqp-bom/3.2.10/spring-amqp-bom-3.2.10.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/batch/spring-batch-bom/5.2.5/spring-batch-bom-5.2.5.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/data/spring-data-bom/2025.0.11/spring-data-bom-2025.0.11.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/spring-framework-bom/6.2.18/spring-framework-bom-6.2.18.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/integration/spring-integration-bom/6.5.8/spring-integration-bom-6.5.8.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/pulsar/spring-pulsar-bom/1.2.17/spring-pulsar-bom-1.2.17.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/restdocs/spring-restdocs-bom/3.0.5/spring-restdocs-bom-3.0.5.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/security/spring-security-bom/6.5.10/spring-security-bom-6.5.10.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/session/spring-session-bom/3.5.6/spring-session-bom-3.5.6.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/ws/spring-ws-bom/4.1.3/spring-ws-bom-4.1.3.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/testcontainers/testcontainers-bom/1.21.4/testcontainers-bom-1.21.4.pom
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.activemq:activemq-bom:pom:6.1.8 (absent): Could not transfer artifact org.apache.activemq:activemq-bom:pom:6.1.8 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2124, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.activemq:artemis-bom:pom:2.40.0 (absent): Could not transfer artifact org.apache.activemq:artemis-bom:pom:2.40.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2131, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.assertj:assertj-bom:pom:3.27.7 (absent): Could not transfer artifact org.assertj:assertj-bom:pom:3.27.7 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2138, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.zipkin.reporter2:zipkin-reporter-bom:pom:3.5.3 (absent): Could not transfer artifact io.zipkin.reporter2:zipkin-reporter-bom:pom:3.5.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2145, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.zipkin.brave:brave-bom:pom:6.1.0 (absent): Could not transfer artifact io.zipkin.brave:brave-bom:pom:6.1.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2152, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.cassandra:java-driver-bom:pom:4.19.2 (absent): Could not transfer artifact org.apache.cassandra:java-driver-bom:pom:4.19.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2159, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.glassfish.jaxb:jaxb-bom:pom:4.0.6 (absent): Could not transfer artifact org.glassfish.jaxb:jaxb-bom:pom:4.0.6 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2166, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.groovy:groovy-bom:pom:4.0.31 (absent): Could not transfer artifact org.apache.groovy:groovy-bom:pom:4.0.31 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2173, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.infinispan:infinispan-bom:pom:15.2.6.Final (absent): Could not transfer artifact org.infinispan:infinispan-bom:pom:15.2.6.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2180, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.fasterxml.jackson:jackson-bom:pom:2.21.2 (absent): Could not transfer artifact com.fasterxml.jackson:jackson-bom:pom:2.21.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2187, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.glassfish.jersey:jersey-bom:pom:3.1.11 (absent): Could not transfer artifact org.glassfish.jersey:jersey-bom:pom:3.1.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2194, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.eclipse.jetty.ee10:jetty-ee10-bom:pom:12.0.34 (absent): Could not transfer artifact org.eclipse.jetty.ee10:jetty-ee10-bom:pom:12.0.34 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2201, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.eclipse.jetty:jetty-bom:pom:12.0.34 (absent): Could not transfer artifact org.eclipse.jetty:jetty-bom:pom:12.0.34 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2208, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.junit:junit-bom:pom:5.12.2 (absent): Could not transfer artifact org.junit:junit-bom:pom:5.12.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2215, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 (absent): Could not transfer artifact org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2222, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlinx:kotlinx-coroutines-bom:pom:1.8.1 (absent): Could not transfer artifact org.jetbrains.kotlinx:kotlinx-coroutines-bom:pom:1.8.1 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2229, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlinx:kotlinx-serialization-bom:pom:1.6.3 (absent): Could not transfer artifact org.jetbrains.kotlinx:kotlinx-serialization-bom:pom:1.6.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2236, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.logging.log4j:log4j-bom:pom:2.24.3 (absent): Could not transfer artifact org.apache.logging.log4j:log4j-bom:pom:2.24.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2243, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.micrometer:micrometer-bom:pom:1.15.11 (absent): Could not transfer artifact io.micrometer:micrometer-bom:pom:1.15.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2250, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.mockito:mockito-bom:pom:5.17.0 (absent): Could not transfer artifact org.mockito:mockito-bom:pom:5.17.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2257, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.mongodb:mongodb-driver-bom:pom:5.5.2 (absent): Could not transfer artifact org.mongodb:mongodb-driver-bom:pom:5.5.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2264, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.netty:netty-bom:pom:4.1.132.Final (absent): Could not transfer artifact io.netty:netty-bom:pom:4.1.132.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2271, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.opentelemetry:opentelemetry-bom:pom:1.49.0 (absent): Could not transfer artifact io.opentelemetry:opentelemetry-bom:pom:1.49.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2278, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.prometheus:prometheus-metrics-bom:pom:1.3.10 (absent): Could not transfer artifact io.prometheus:prometheus-metrics-bom:pom:1.3.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2285, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.prometheus:simpleclient_bom:pom:0.16.0 (absent): Could not transfer artifact io.prometheus:simpleclient_bom:pom:0.16.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2292, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.pulsar:pulsar-bom:pom:4.0.9 (absent): Could not transfer artifact org.apache.pulsar:pulsar-bom:pom:4.0.9 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2299, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.pulsar:pulsar-client-reactive-bom:pom:0.6.0 (absent): Could not transfer artifact org.apache.pulsar:pulsar-client-reactive-bom:pom:0.6.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2306, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.querydsl:querydsl-bom:pom:5.1.0 (absent): Could not transfer artifact com.querydsl:querydsl-bom:pom:5.1.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2313, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.projectreactor:reactor-bom:pom:2024.0.17 (absent): Could not transfer artifact io.projectreactor:reactor-bom:pom:2024.0.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2320, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.rest-assured:rest-assured-bom:pom:5.5.7 (absent): Could not transfer artifact io.rest-assured:rest-assured-bom:pom:5.5.7 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2327, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.rsocket:rsocket-bom:pom:1.1.5 (absent): Could not transfer artifact io.rsocket:rsocket-bom:pom:1.1.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2334, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.seleniumhq.selenium:selenium-bom:pom:4.31.0 (absent): Could not transfer artifact org.seleniumhq.selenium:selenium-bom:pom:4.31.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2341, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.amqp:spring-amqp-bom:pom:3.2.10 (absent): Could not transfer artifact org.springframework.amqp:spring-amqp-bom:pom:3.2.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2348, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.batch:spring-batch-bom:pom:5.2.5 (absent): Could not transfer artifact org.springframework.batch:spring-batch-bom:pom:5.2.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2355, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.data:spring-data-bom:pom:2025.0.11 (absent): Could not transfer artifact org.springframework.data:spring-data-bom:pom:2025.0.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2362, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework:spring-framework-bom:pom:6.2.18 (absent): Could not transfer artifact org.springframework:spring-framework-bom:pom:6.2.18 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2369, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.integration:spring-integration-bom:pom:6.5.8 (absent): Could not transfer artifact org.springframework.integration:spring-integration-bom:pom:6.5.8 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2376, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.pulsar:spring-pulsar-bom:pom:1.2.17 (absent): Could not transfer artifact org.springframework.pulsar:spring-pulsar-bom:pom:1.2.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2383, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.restdocs:spring-restdocs-bom:pom:3.0.5 (absent): Could not transfer artifact org.springframework.restdocs:spring-restdocs-bom:pom:3.0.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2390, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.security:spring-security-bom:pom:6.5.10 (absent): Could not transfer artifact org.springframework.security:spring-security-bom:pom:6.5.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2397, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.session:spring-session-bom:pom:3.5.6 (absent): Could not transfer artifact org.springframework.session:spring-session-bom:pom:3.5.6 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2404, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.ws:spring-ws-bom:pom:4.1.3 (absent): Could not transfer artifact org.springframework.ws:spring-ws-bom:pom:4.1.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2411, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.testcontainers:testcontainers-bom:pom:1.21.4 (absent): Could not transfer artifact org.testcontainers:testcontainers-bom:pom:1.21.4 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2418, column 19
@
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project com.tracexd:tracexd:1.0.0 (C:\Work\tracexd\pom.xml) has 43 errors
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.activemq:activemq-bom:pom:6.1.8 (absent): Could not transfer artifact org.apache.activemq:activemq-bom:pom:6.1.8 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2124, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.activemq:artemis-bom:pom:2.40.0 (absent): Could not transfer artifact org.apache.activemq:artemis-bom:pom:2.40.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2131, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.assertj:assertj-bom:pom:3.27.7 (absent): Could not transfer artifact org.assertj:assertj-bom:pom:3.27.7 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2138, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.zipkin.reporter2:zipkin-reporter-bom:pom:3.5.3 (absent): Could not transfer artifact io.zipkin.reporter2:zipkin-reporter-bom:pom:3.5.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2145, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.zipkin.brave:brave-bom:pom:6.1.0 (absent): Could not transfer artifact io.zipkin.brave:brave-bom:pom:6.1.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2152, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.cassandra:java-driver-bom:pom:4.19.2 (absent): Could not transfer artifact org.apache.cassandra:java-driver-bom:pom:4.19.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2159, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.glassfish.jaxb:jaxb-bom:pom:4.0.6 (absent): Could not transfer artifact org.glassfish.jaxb:jaxb-bom:pom:4.0.6 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2166, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.groovy:groovy-bom:pom:4.0.31 (absent): Could not transfer artifact org.apache.groovy:groovy-bom:pom:4.0.31 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2173, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.infinispan:infinispan-bom:pom:15.2.6.Final (absent): Could not transfer artifact org.infinispan:infinispan-bom:pom:15.2.6.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2180, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.fasterxml.jackson:jackson-bom:pom:2.21.2 (absent): Could not transfer artifact com.fasterxml.jackson:jackson-bom:pom:2.21.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2187, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.glassfish.jersey:jersey-bom:pom:3.1.11 (absent): Could not transfer artifact org.glassfish.jersey:jersey-bom:pom:3.1.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2194, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.eclipse.jetty.ee10:jetty-ee10-bom:pom:12.0.34 (absent): Could not transfer artifact org.eclipse.jetty.ee10:jetty-ee10-bom:pom:12.0.34 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2201, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.eclipse.jetty:jetty-bom:pom:12.0.34 (absent): Could not transfer artifact org.eclipse.jetty:jetty-bom:pom:12.0.34 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2208, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.junit:junit-bom:pom:5.12.2 (absent): Could not transfer artifact org.junit:junit-bom:pom:5.12.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2215, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 (absent): Could not transfer artifact org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2222, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlinx:kotlinx-coroutines-bom:pom:1.8.1 (absent): Could not transfer artifact org.jetbrains.kotlinx:kotlinx-coroutines-bom:pom:1.8.1 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2229, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlinx:kotlinx-serialization-bom:pom:1.6.3 (absent): Could not transfer artifact org.jetbrains.kotlinx:kotlinx-serialization-bom:pom:1.6.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2236, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.logging.log4j:log4j-bom:pom:2.24.3 (absent): Could not transfer artifact org.apache.logging.log4j:log4j-bom:pom:2.24.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2243, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.micrometer:micrometer-bom:pom:1.15.11 (absent): Could not transfer artifact io.micrometer:micrometer-bom:pom:1.15.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2250, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.mockito:mockito-bom:pom:5.17.0 (absent): Could not transfer artifact org.mockito:mockito-bom:pom:5.17.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2257, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.mongodb:mongodb-driver-bom:pom:5.5.2 (absent): Could not transfer artifact org.mongodb:mongodb-driver-bom:pom:5.5.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2264, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.netty:netty-bom:pom:4.1.132.Final (absent): Could not transfer artifact io.netty:netty-bom:pom:4.1.132.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2271, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.opentelemetry:opentelemetry-bom:pom:1.49.0 (absent): Could not transfer artifact io.opentelemetry:opentelemetry-bom:pom:1.49.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2278, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.prometheus:prometheus-metrics-bom:pom:1.3.10 (absent): Could not transfer artifact io.prometheus:prometheus-metrics-bom:pom:1.3.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2285, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.prometheus:simpleclient_bom:pom:0.16.0 (absent): Could not transfer artifact io.prometheus:simpleclient_bom:pom:0.16.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2292, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.pulsar:pulsar-bom:pom:4.0.9 (absent): Could not transfer artifact org.apache.pulsar:pulsar-bom:pom:4.0.9 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2299, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.pulsar:pulsar-client-reactive-bom:pom:0.6.0 (absent): Could not transfer artifact org.apache.pulsar:pulsar-client-reactive-bom:pom:0.6.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2306, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.querydsl:querydsl-bom:pom:5.1.0 (absent): Could not transfer artifact com.querydsl:querydsl-bom:pom:5.1.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2313, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.projectreactor:reactor-bom:pom:2024.0.17 (absent): Could not transfer artifact io.projectreactor:reactor-bom:pom:2024.0.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2320, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.rest-assured:rest-assured-bom:pom:5.5.7 (absent): Could not transfer artifact io.rest-assured:rest-assured-bom:pom:5.5.7 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2327, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.rsocket:rsocket-bom:pom:1.1.5 (absent): Could not transfer artifact io.rsocket:rsocket-bom:pom:1.1.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2334, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.seleniumhq.selenium:selenium-bom:pom:4.31.0 (absent): Could not transfer artifact org.seleniumhq.selenium:selenium-bom:pom:4.31.0 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2341, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.amqp:spring-amqp-bom:pom:3.2.10 (absent): Could not transfer artifact org.springframework.amqp:spring-amqp-bom:pom:3.2.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2348, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.batch:spring-batch-bom:pom:5.2.5 (absent): Could not transfer artifact org.springframework.batch:spring-batch-bom:pom:5.2.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2355, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.data:spring-data-bom:pom:2025.0.11 (absent): Could not transfer artifact org.springframework.data:spring-data-bom:pom:2025.0.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2362, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework:spring-framework-bom:pom:6.2.18 (absent): Could not transfer artifact org.springframework:spring-framework-bom:pom:6.2.18 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2369, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.integration:spring-integration-bom:pom:6.5.8 (absent): Could not transfer artifact org.springframework.integration:spring-integration-bom:pom:6.5.8 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2376, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.pulsar:spring-pulsar-bom:pom:1.2.17 (absent): Could not transfer artifact org.springframework.pulsar:spring-pulsar-bom:pom:1.2.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2383, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.restdocs:spring-restdocs-bom:pom:3.0.5 (absent): Could not transfer artifact org.springframework.restdocs:spring-restdocs-bom:pom:3.0.5 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2390, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.security:spring-security-bom:pom:6.5.10 (absent): Could not transfer artifact org.springframework.security:spring-security-bom:pom:6.5.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2397, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.session:spring-session-bom:pom:3.5.6 (absent): Could not transfer artifact org.springframework.session:spring-session-bom:pom:3.5.6 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2404, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.ws:spring-ws-bom:pom:4.1.3 (absent): Could not transfer artifact org.springframework.ws:spring-ws-bom:pom:4.1.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2411, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.testcontainers:testcontainers-bom:pom:1.21.4 (absent): Could not transfer artifact org.testcontainers:testcontainers-bom:pom:1.21.4 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2418, column 19 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException

63
build2.log Normal file
View File

@ -0,0 +1,63 @@
mvn : WARNING: A restricted method in java.lang.System has been called
所在位置 行:2 字符: 1
+ mvn compile 2>&1 | Out-File "C:\Work\tracexd\build2.log" -Encoding ut ...
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (WARNING: A rest...has been called:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file
:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/jansi-2.4.1
.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeA
tomicHelper (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.
9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$Un
safeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
WARNING: Final field _cipher in class org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher has been mutat
ed reflectively by class org.eclipse.sisu.bean.BeanPropertyField in unnamed module @780cb77 (file:/C:/Users/16332/.m2/w
rapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/org.eclipse.sisu.inject-0.9.0.M3.
jar)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
[INFO] Scanning for projects...
Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-bom/2.21.2/jackson-bom-2.21.2.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-bom/1.9.25/kotlin-bom-1.9.25.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/logging/log4j/log4j-bom/2.24.3/log4j-bom-2.24.3.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/micrometer/micrometer-bom/1.15.11/micrometer-bom-1.15.11.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/netty/netty-bom/4.1.132.Final/netty-bom-4.1.132.Final.pom
Downloading from central: https://repo.maven.apache.org/maven2/io/projectreactor/reactor-bom/2024.0.17/reactor-bom-2024.0.17.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/spring-framework-bom/6.2.18/spring-framework-bom-6.2.18.pom
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/security/spring-security-bom/6.5.10/spring-security-bom-6.5.10.pom
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.fasterxml.jackson:jackson-bom:pom:2.21.2 (absent): Could not transfer artifact com.fasterxml.jackson:jackson-bom:pom:2.21.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2133, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 (absent): Could not transfer artifact org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2144, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.logging.log4j:log4j-bom:pom:2.24.3 (absent): Could not transfer artifact org.apache.logging.log4j:log4j-bom:pom:2.24.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2153, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.micrometer:micrometer-bom:pom:1.15.11 (absent): Could not transfer artifact io.micrometer:micrometer-bom:pom:1.15.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2160, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.netty:netty-bom:pom:4.1.132.Final (absent): Could not transfer artifact io.netty:netty-bom:pom:4.1.132.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2169, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.projectreactor:reactor-bom:pom:2024.0.17 (absent): Could not transfer artifact io.projectreactor:reactor-bom:pom:2024.0.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2182, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework:spring-framework-bom:pom:6.2.18 (absent): Could not transfer artifact org.springframework:spring-framework-bom:pom:6.2.18 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2195, column 19
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.security:spring-security-bom:pom:6.5.10 (absent): Could not transfer artifact org.springframework.security:spring-security-bom:pom:6.5.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2205, column 19
@
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project com.tracexd:tracexd:1.0.0 (C:\Work\tracexd\pom.xml) has 8 errors
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.fasterxml.jackson:jackson-bom:pom:2.21.2 (absent): Could not transfer artifact com.fasterxml.jackson:jackson-bom:pom:2.21.2 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2133, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 (absent): Could not transfer artifact org.jetbrains.kotlin:kotlin-bom:pom:1.9.25 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2144, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.apache.logging.log4j:log4j-bom:pom:2.24.3 (absent): Could not transfer artifact org.apache.logging.log4j:log4j-bom:pom:2.24.3 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2153, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.micrometer:micrometer-bom:pom:1.15.11 (absent): Could not transfer artifact io.micrometer:micrometer-bom:pom:1.15.11 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2160, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.netty:netty-bom:pom:4.1.132.Final (absent): Could not transfer artifact io.netty:netty-bom:pom:4.1.132.Final from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2169, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: io.projectreactor:reactor-bom:pom:2024.0.17 (absent): Could not transfer artifact io.projectreactor:reactor-bom:pom:2024.0.17 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2182, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework:spring-framework-bom:pom:6.2.18 (absent): Could not transfer artifact org.springframework:spring-framework-bom:pom:6.2.18 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2195, column 19 -> [Help 2]
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.springframework.security:spring-security-bom:pom:6.5.10 (absent): Could not transfer artifact org.springframework.security:spring-security-bom:pom:6.5.10 from/to central (https://repo.maven.apache.org/maven2): Permission denied: getsockopt @ org.springframework.boot:spring-boot-dependencies:3.5.14, C:\Work\tracexd\spring-boot-dependencies-3.5.14.pom, line 2205, column 19 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException

68
pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.14</version>
<relativePath/>
</parent>
<groupId>com.tracexd</groupId>
<artifactId>tracexd</artifactId>
<version>1.0.0</version>
<name>tracexd</name>
<description>Daily events recording and querying system</description>
<properties>
<java.version>26</java.version>
<mybatis-plus.version>3.5.12</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

69
run.log Normal file
View File

@ -0,0 +1,69 @@
mvn : WARNING: A restricted method in java.lang.System has been called
At line:2 char:37
+ $env:SPRING_PROFILES_ACTIVE="test"; mvn spring-boot:run 2>&1 | Out-Fi ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (WARNING: A rest...has been called:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file
:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/jansi-2.4.1
.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeA
tomicHelper (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.
9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$Un
safeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
WARNING: Final field _cipher in class org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher has been mutat
ed reflectively by class org.eclipse.sisu.bean.BeanPropertyField in unnamed module @780cb77 (file:/C:/Users/16332/.m2/w
rapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/org.eclipse.sisu.inject-0.9.0.M3.
jar)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.tracexd:tracexd >-------------------------
[INFO] Building tracexd 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot:3.5.14:run (default-cli) > test-compile @ tracexd >>>
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ tracexd ---
[INFO] Copying 2 resources from src\main\resources to target\classes
[INFO] Copying 6 resources from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.14.1:compile (default-compile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ tracexd ---
[INFO] skip non existing resourceDirectory C:\Work\tracexd\src\test\resources
[INFO]
[INFO] --- compiler:3.14.1:testCompile (default-testCompile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] <<< spring-boot:3.5.14:run (default-cli) < test-compile @ tracexd <<<
[INFO]
[INFO]
[INFO] --- spring-boot:3.5.14:run (default-cli) @ tracexd ---
[INFO] Attaching agents: []
2026-06-09T14:04:19.923+08:00 INFO 10212 --- [ main] com.tracexd.TracexdApplication : Starting TracexdApplication using Java 26.0.1 with PID 10212 (C:\Work\tracexd\target\classes started by 16332 in C:\Work\tracexd)
2026-06-09T14:04:19.924+08:00 DEBUG 10212 --- [ main] com.tracexd.TracexdApplication : Running with Spring Boot v3.5.14, Spring v6.2.18
2026-06-09T14:04:19.924+08:00 INFO 10212 --- [ main] com.tracexd.TracexdApplication : The following 1 profile is active: "test"
2026-06-09T14:04:20.498+08:00 INFO 10212 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-06-09T14:04:20.498+08:00 INFO 10212 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.54]
2026-06-09T14:04:20.525+08:00 INFO 10212 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Get /198.18.0.1 network interface
Get network interface info: name:iftype53_32769 (Meta Tunnel)
Initialization Sequence datacenterId:0 workerId:4
2026-06-09T14:04:20.621+08:00 INFO 10212 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-06-09T14:04:20.717+08:00 INFO 10212 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:tracexd user=SA
2026-06-09T14:04:20.718+08:00 INFO 10212 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-06-09T14:04:21.123+08:00 INFO 10212 --- [ main] m.e.s.MybatisPlusApplicationContextAware : Register ApplicationContext instances org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@e383572
2026-06-09T14:04:21.162+08:00 INFO 10212 --- [ main] com.tracexd.TracexdApplication : Started TracexdApplication in 1.492 seconds (process running for 1.661)
Default admin user created (admin/admin123)

View File

@ -0,0 +1,41 @@
package com.tracexd;
import com.tracexd.mapper.RecordMapper;
import com.tracexd.model.entity.Record;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 日常事项记录系统 - 启动类
*/
@SpringBootApplication
@EnableAsync
public class TracexdApplication {
public static void main(String[] args) {
SpringApplication.run(TracexdApplication.class, args);
}
/**
* 初始化默认管理员用户首次启动时创建
*/
@Bean
public CommandLineRunner initAdminUser(PasswordEncoder passwordEncoder, JdbcTemplate jdbcTemplate) {
return args -> {
Long count = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM sys_user WHERE username = ?", Long.class, "admin");
if (count == null || count == 0) {
String hash = passwordEncoder.encode("admin123");
jdbcTemplate.update(
"INSERT INTO sys_user (username, password, display_name, enabled, created_at) VALUES (?, ?, ?, 1, NOW())",
"admin", hash, "管理员");
System.out.println("Default admin user created (admin/admin123)");
}
};
}
}

View File

@ -0,0 +1,44 @@
package com.tracexd.common;
/**
* 统一API响应体
*/
public class Result<T> {
private int code;
private String message;
private T data;
private Result() {
}
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.code = 200;
r.message = "success";
r.data = data;
return r;
}
public static <T> Result<T> ok() {
return ok(null);
}
public static <T> Result<T> error(int code, String message) {
Result<T> r = new Result<>();
r.code = code;
r.message = message;
return r;
}
public static <T> Result<T> error(String message) {
return error(500, message);
}
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}

View File

@ -0,0 +1,60 @@
package com.tracexd.common;
import com.tracexd.model.dto.VoiceProcessResult;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* 语音任务管理器 管理异步任务和 SSE 推送
*/
@Component
public class VoiceTaskManager {
private final Map<String, CompletableFuture<VoiceProcessResult>> taskFutures = new ConcurrentHashMap<>();
private final Map<String, SseEmitter> taskEmitters = new ConcurrentHashMap<>();
/**
* 注册一个任务
*
* @param taskId 任务ID
* @param future 异步处理结果
*/
public void registerTask(String taskId, CompletableFuture<VoiceProcessResult> future) {
taskFutures.put(taskId, future);
future.whenComplete((result, ex) -> {
SseEmitter emitter = taskEmitters.remove(taskId);
if (emitter != null) {
try {
if (ex != null) {
emitter.send(SseEmitter.event().data(Result.error(ex.getMessage())));
} else {
emitter.send(SseEmitter.event().data(Result.ok(result)));
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
}
taskFutures.remove(taskId);
});
}
/**
* 为任务创建 SSE 连接
*
* @param taskId 任务ID
* @return SSE 发射器
*/
public SseEmitter createEmitter(String taskId) {
SseEmitter emitter = new SseEmitter(120_000L);
taskEmitters.put(taskId, emitter);
emitter.onCompletion(() -> taskEmitters.remove(taskId));
emitter.onTimeout(() -> taskEmitters.remove(taskId));
return emitter;
}
}

View File

@ -0,0 +1,22 @@
package com.tracexd.common.exception;
/**
* 业务异常
*/
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(String message) {
this(500, message);
}
public int getCode() {
return code;
}
}

View File

@ -0,0 +1,28 @@
package com.tracexd.common.exception;
import com.tracexd.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
log.warn("Business exception: {}", e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("Unexpected error", e);
return Result.error("系统繁忙,请稍后重试");
}
}

View File

@ -0,0 +1,27 @@
package com.tracexd.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步任务线程池配置
*/
@Configuration
public class AsyncConfig {
@Bean("voiceTaskExecutor")
public Executor voiceTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(16);
executor.setThreadNamePrefix("voice-task-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,22 @@
package com.tracexd.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis-Plus 配置
*/
@Configuration
@MapperScan("com.tracexd.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}

View File

@ -0,0 +1,17 @@
package com.tracexd.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
/**
* HTTP 客户端配置
*/
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient() {
return RestClient.builder().build();
}
}

View File

@ -0,0 +1,88 @@
package com.tracexd.config;
import com.tracexd.mapper.SysUserMapper;
import com.tracexd.model.entity.SysUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
/**
* Spring Security 安全配置
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final DataSource dataSource;
private final SysUserMapper sysUserMapper;
public SecurityConfig(DataSource dataSource, SysUserMapper sysUserMapper) {
this.dataSource = dataSource;
this.sysUserMapper = sysUserMapper;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/css/**", "/js/**", "/.well-known/**", "/error").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.rememberMe(remember -> remember
.tokenValiditySeconds(30 * 24 * 60 * 60)
.tokenRepository(persistentTokenRepository())
)
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
);
return http.build();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return username -> {
SysUser user = sysUserMapper.findByUsername(username);
if (user == null || !user.getEnabled()) {
throw new UsernameNotFoundException("User not found: " + username);
}
return User.withUsername(user.getUsername())
.password(user.getPassword())
.disabled(!user.getEnabled())
.build();
};
}
}

View File

@ -0,0 +1,40 @@
package com.tracexd.controller;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
/**
* 页面路由控制器
*/
@Controller
public class PageController {
/**
* 首页麦克风录入/查询页面
*/
@GetMapping("/")
public String index(Model model, Authentication auth) {
if (auth != null && auth.isAuthenticated()) {
model.addAttribute("displayName", auth.getName());
}
return "index";
}
/**
* 浏览记录页面
*/
@GetMapping("/browse")
public String browse() {
return "browse";
}
/**
* 登录页面
*/
@GetMapping("/login")
public String login() {
return "login";
}
}

View File

@ -0,0 +1,61 @@
package com.tracexd.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.tracexd.common.Result;
import com.tracexd.enums.DictTypeEnum;
import com.tracexd.model.dto.RecordQueryDTO;
import com.tracexd.model.entity.Record;
import com.tracexd.service.record.DictTagService;
import com.tracexd.service.record.RecordService;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 记录查询控制器浏览页
*/
@RestController
@RequestMapping("/api/records")
public class RecordController {
private final RecordService recordService;
private final DictTagService dictTagService;
public RecordController(RecordService recordService, DictTagService dictTagService) {
this.recordService = recordService;
this.dictTagService = dictTagService;
}
@GetMapping
public Result<Map<String, Object>> queryRecords(RecordQueryDTO queryDTO, Authentication auth) {
Long userId = getUserIdFromAuth(auth);
IPage<Record> page = recordService.queryPage(queryDTO, userId);
BigDecimal totalAmount = recordService.sumAmount(queryDTO, userId);
Map<String, Object> result = new HashMap<>();
result.put("records", page.getRecords());
result.put("total", page.getTotal());
result.put("page", page.getCurrent());
result.put("size", page.getSize());
result.put("totalAmount", totalAmount);
return Result.ok(result);
}
@GetMapping("/filters")
public Result<Map<String, List<String>>> getFilters(Authentication auth) {
Long userId = getUserIdFromAuth(auth);
Map<String, List<String>> filters = new HashMap<>();
filters.put("persons", dictTagService.getTagsByType(userId, DictTypeEnum.PERSON.getCode()));
filters.put("locations", dictTagService.getTagsByType(userId, DictTypeEnum.LOCATION.getCode()));
filters.put("categories", dictTagService.getTagsByType(userId, DictTypeEnum.CATEGORY.getCode()));
return Result.ok(filters);
}
private Long getUserIdFromAuth(Authentication auth) {
return 1L;
}
}

View File

@ -0,0 +1,76 @@
package com.tracexd.controller;
import com.tracexd.common.Result;
import com.tracexd.common.VoiceTaskManager;
import com.tracexd.service.voice.VoiceProcessService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* 语音处理控制器
*/
@RestController
@RequestMapping("/api/voice")
public class VoiceController {
private static final Logger log = LoggerFactory.getLogger(VoiceController.class);
private final VoiceProcessService voiceProcessService;
private final VoiceTaskManager voiceTaskManager;
public VoiceController(VoiceProcessService voiceProcessService, VoiceTaskManager voiceTaskManager) {
this.voiceProcessService = voiceProcessService;
this.voiceTaskManager = voiceTaskManager;
}
/**
* 提交语音进行识别和处理
*/
@PostMapping("/process")
public Result<Map<String, String>> processVoice(
@RequestParam("audio") MultipartFile audio,
Authentication auth) {
String contentType = audio.getContentType();
String mimeType = (contentType != null) ? contentType : "audio/wav";
log.info("Voice request from user: {}, file size: {}, mime: {}", auth.getName(), audio.getSize(), mimeType);
Long userId = getUserIdFromAuth(auth);
try {
byte[] audioData = audio.getBytes();
String taskId = UUID.randomUUID().toString();
CompletableFuture<?> future = voiceProcessService.processVoice(audioData, mimeType, userId);
voiceTaskManager.registerTask(taskId, (CompletableFuture) future);
return Result.ok(Map.of("taskId", taskId));
} catch (IOException e) {
log.error("Failed to read audio", e);
return Result.error("读取音频失败");
}
}
/**
* SSE 订阅处理结果
*/
@GetMapping(value = "/stream/{taskId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamResult(@PathVariable String taskId) {
return voiceTaskManager.createEmitter(taskId);
}
private Long getUserIdFromAuth(Authentication auth) {
return 1L;
}
}

View File

@ -0,0 +1,26 @@
package com.tracexd.enums;
/**
* 字典标签类型枚举
*/
public enum DictTypeEnum {
/** 人物 */
PERSON("person"),
/** 地点 */
LOCATION("location"),
/** 分类 */
CATEGORY("category");
private final String code;
DictTypeEnum(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}

View File

@ -0,0 +1,16 @@
package com.tracexd.enums;
/**
* AI大模型意图类型枚举
*/
public enum IntentType {
/** 录入事项 */
RECORD,
/** 查询事项 */
QUERY,
/** 其他对话 */
OTHER
}

View File

@ -0,0 +1,10 @@
package com.tracexd.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tracexd.model.entity.DictTag;
/**
* 字典标签 Mapper
*/
public interface DictTagMapper extends BaseMapper<DictTag> {
}

View File

@ -0,0 +1,10 @@
package com.tracexd.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tracexd.model.entity.Record;
/**
* 事项记录 Mapper
*/
public interface RecordMapper extends BaseMapper<Record> {
}

View File

@ -0,0 +1,17 @@
package com.tracexd.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tracexd.model.entity.SysUser;
/**
* 系统用户 Mapper
*/
public interface SysUserMapper extends BaseMapper<SysUser> {
/** 根据用户名查找用户 */
default SysUser findByUsername(String username) {
return selectOne(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUsername, username));
}
}

View File

@ -0,0 +1,33 @@
package com.tracexd.model.dto;
import java.time.LocalDate;
import java.util.List;
/**
* 记录查询条件 DTO
*/
public class RecordQueryDTO {
private List<String> persons;
private List<String> locations;
private List<String> categories;
private LocalDate startDate;
private LocalDate endDate;
private int page = 1;
private int size = 20;
public List<String> getPersons() { return persons; }
public void setPersons(List<String> persons) { this.persons = persons; }
public List<String> getLocations() { return locations; }
public void setLocations(List<String> locations) { this.locations = locations; }
public List<String> getCategories() { return categories; }
public void setCategories(List<String> categories) { this.categories = categories; }
public LocalDate getStartDate() { return startDate; }
public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
public LocalDate getEndDate() { return endDate; }
public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
public int getPage() { return page; }
public void setPage(int page) { this.page = page; }
public int getSize() { return size; }
public void setSize(int size) { this.size = size; }
}

View File

@ -0,0 +1,35 @@
package com.tracexd.model.dto;
import com.tracexd.enums.IntentType;
import java.util.List;
import java.util.Map;
/**
* 语音处理结果 DTO
*/
public class VoiceProcessResult {
private IntentType intent;
private String asrText;
private String reply;
private String recordTip;
private List<Map<String, Object>> queryData;
private String sql;
private boolean success;
public IntentType getIntent() { return intent; }
public void setIntent(IntentType intent) { this.intent = intent; }
public String getAsrText() { return asrText; }
public void setAsrText(String asrText) { this.asrText = asrText; }
public String getReply() { return reply; }
public void setReply(String reply) { this.reply = reply; }
public String getRecordTip() { return recordTip; }
public void setRecordTip(String recordTip) { this.recordTip = recordTip; }
public List<Map<String, Object>> getQueryData() { return queryData; }
public void setQueryData(List<Map<String, Object>> queryData) { this.queryData = queryData; }
public String getSql() { return sql; }
public void setSql(String sql) { this.sql = sql; }
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
}

View File

@ -0,0 +1,36 @@
package com.tracexd.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDateTime;
/**
* 字典标签表实体统一存储人物地点分类
*/
@TableName("dict_tag")
public class DictTag {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String type;
private String name;
private LocalDateTime createdAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

View File

@ -0,0 +1,53 @@
package com.tracexd.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 日常事项记录表实体
*/
@TableName("record")
public class Record {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String person;
private LocalDateTime time;
private String location;
private String content;
private BigDecimal amount;
private String category;
private LocalDateTime createdAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getPerson() { return person; }
public void setPerson(String person) { this.person = person; }
public LocalDateTime getTime() { return time; }
public void setTime(LocalDateTime time) { this.time = time; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

View File

@ -0,0 +1,40 @@
package com.tracexd.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDateTime;
/**
* 系统用户表实体
*/
@TableName("sys_user")
public class SysUser {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String displayName;
private Boolean enabled;
private LocalDateTime createdAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getDisplayName() { return displayName; }
public void setDisplayName(String displayName) { this.displayName = displayName; }
public Boolean getEnabled() { return enabled; }
public void setEnabled(Boolean enabled) { this.enabled = enabled; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

View File

@ -0,0 +1,164 @@
package com.tracexd.service.ai;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tracexd.common.exception.BusinessException;
import com.tracexd.enums.IntentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import java.util.List;
import java.util.Map;
/**
* AI 大模型服务 调用 Deepseek API
*/
@Service
public class AiService {
private static final Logger log = LoggerFactory.getLogger(AiService.class);
private static final String RECORD_SYSTEM_PROMPT = """
你是一个日常事项管理AI用户会输入语音转写后的文本请分析用户意图
情况1 - 录入事项
用户描述了"谁在什么地方花多少钱干了什么"这类日常消费或活动
请生成 INSERT INTO record 语句字段person人物默认""time事项发生时间用用户提到的时间或当前时间location地点content事项描述amount金额没提到则0category2~4字分类如吃饭剪头加油修车购物旅游交通话费医疗水电等
返回JSON{"intent":"record","sql":"INSERT SQL语句","reply":""}
情况2 - 查询事项
用户在询问历史记录例如"我昨天做了什么""我一共花了多少钱""我上次吃面是什么时候"
生成 SELECT 语句只查 person, time, content, amount 四个字段
返回JSON{"intent":"query","sql":"SELECT SQL语句","reply":""}
情况3 - 其他
用户说了录入或查询以外的内容
返回JSON{"intent":"other","sql":"","reply":"友善的回复"}
只返回JSON不要包含其他内容当前时间{currentTime}
""";
private static final String SUMMARY_PROMPT = """
用户的问题: {userText}
查询到以下数据:
{queryResult}
请根据查询结果用中文生成一段简洁易懂的回复总结用户想知道的息
如果结果为空请友好告知没有找到相关记录
""";
private final RestClient restClient;
private final ObjectMapper objectMapper;
@Value("${tracexd.deepseek.api-key}")
private String apiKey;
@Value("${tracexd.deepseek.base-url}")
private String baseUrl;
@Value("${tracexd.deepseek.model}")
private String model;
public AiService(RestClient restClient, ObjectMapper objectMapper) {
this.restClient = restClient;
this.objectMapper = objectMapper;
}
/**
* 分析用户输入文本的意图并生成相应 SQL 或回复
*
* @param userText ASR 识别后的文本
* @return 包含意图SQL回复的分析结果
*/
public IntentResult analyzeIntent(String userText) {
String systemPrompt = RECORD_SYSTEM_PROMPT.replace("{currentTime}", java.time.LocalDateTime.now().toString());
String responseContent = callDeepseek(systemPrompt, userText);
return parseIntentResult(responseContent);
}
/**
* 将查询结果发给 AI 生成总结
*
* @param userText 用户原始输入
* @param queryResult 查询到的数据 JSON
* @return AI 生成的总结回复
*/
public String summarizeQueryResult(String userText, String queryResult) {
String prompt = SUMMARY_PROMPT
.replace("{userText}", userText)
.replace("{queryResult}", queryResult);
return callDeepseek("你是一个数据总结助手", prompt);
}
/**
* 通用方法调用 Deepseek API
*/
private String callDeepseek(String systemPrompt, String userText) {
Map<String, Object> requestBody = Map.of(
"model", model,
"messages", List.of(
Map.of("role", "system", "content", systemPrompt),
Map.of("role", "user", "content", userText)
),
"stream", false
);
log.info("Calling Deepseek model: {}", model);
Map result = restClient.post()
.uri(baseUrl + "/chat/completions")
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.body(requestBody)
.retrieve()
.body(Map.class);
if (result == null || !result.containsKey("choices")) {
throw new BusinessException("AI 调用失败:无返回结果");
}
List<Map<String, Object>> choices = (List<Map<String, Object>>) result.get("choices");
if (choices == null || choices.isEmpty()) {
throw new BusinessException("AI 调用失败:无有效结果");
}
Map<String, Object> message = (Map<String, Object>) choices.get(0).get("message");
return (String) message.get("content");
}
/**
* 解析 AI 返回的 JSON 结果
*/
private IntentResult parseIntentResult(String json) {
try {
JsonNode root = objectMapper.readTree(json);
String intentStr = root.get("intent").asText();
String sql = root.has("sql") ? root.get("sql").asText() : "";
String reply = root.has("reply") ? root.get("reply").asText() : "";
IntentType intent = switch (intentStr) {
case "record" -> IntentType.RECORD;
case "query" -> IntentType.QUERY;
default -> IntentType.OTHER;
};
return new IntentResult(intent, sql, reply);
} catch (JsonProcessingException e) {
log.error("Failed to parse AI response: {}", json, e);
throw new BusinessException("AI 返回格式异常");
}
}
/**
* 意图分析结果内部类
*/
public record IntentResult(IntentType intent, String sql, String reply) {
}
}

View File

@ -0,0 +1,85 @@
package com.tracexd.service.asr;
import com.tracexd.common.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import java.util.Base64;
import java.util.List;
import java.util.Map;
/**
* ASR 语音识别服务 调用小米 MIMO V2.5 ASR
*/
@Service
public class AsrService {
private static final Logger log = LoggerFactory.getLogger(AsrService.class);
private final RestClient restClient;
@Value("${tracexd.mimo.api-key}")
private String apiKey;
@Value("${tracexd.mimo.base-url}")
private String baseUrl;
@Value("${tracexd.mimo.model}")
private String model;
public AsrService(RestClient restClient) {
this.restClient = restClient;
}
/**
* 对音频进行语音识别
*
* @param audioData 音频原始字节
* @param mimeType 音频 MIME 类型audio/wav audio/mpeg
* @return 识别后的文本
*/
public String recognize(byte[] audioData, String mimeType) {
String base64Audio = Base64.getEncoder().encodeToString(audioData);
Map<String, Object> requestBody = Map.of(
"model", model,
"messages", List.of(Map.of(
"role", "user",
"content", List.of(Map.of(
"type", "input_audio",
"input_audio", Map.of(
"data", "data:" + mimeType + ";base64," + base64Audio
)
))
)),
"asr_options", Map.of("language", "zh")
);
log.info("Calling MIMO ASR, audio size: {} bytes, mime: {}", audioData.length, mimeType);
Map result = restClient.post()
.uri(baseUrl + "/chat/completions")
.header("api-key", apiKey)
.header("Content-Type", "application/json")
.body(requestBody)
.retrieve()
.body(Map.class);
if (result == null || !result.containsKey("choices")) {
throw new BusinessException("ASR 识别失败:无返回结果");
}
List<Map<String, Object>> choices = (List<Map<String, Object>>) result.get("choices");
if (choices == null || choices.isEmpty()) {
throw new BusinessException("ASR 识别失败:无有效结果");
}
Map<String, Object> message = (Map<String, Object>) choices.get(0).get("message");
String text = (String) message.get("content");
log.info("ASR result: {}", text);
return text;
}
}

View File

@ -0,0 +1,56 @@
package com.tracexd.service.record;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tracexd.mapper.DictTagMapper;
import com.tracexd.model.entity.DictTag;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 字典标签服务 管理人物/地点/分类的去重存储
*/
@Service
public class DictTagService {
private final DictTagMapper dictTagMapper;
public DictTagService(DictTagMapper dictTagMapper) {
this.dictTagMapper = dictTagMapper;
}
/**
* 确保标签存在于字典中不存在则插入
*/
public void ensureTagExists(Long userId, String type, String name) {
if (name == null || name.isBlank()) return;
Long count = dictTagMapper.selectCount(
new LambdaQueryWrapper<DictTag>()
.eq(DictTag::getUserId, userId)
.eq(DictTag::getType, type)
.eq(DictTag::getName, name)
);
if (count == null || count == 0) {
DictTag tag = new DictTag();
tag.setUserId(userId);
tag.setType(type);
tag.setName(name);
tag.setCreatedAt(LocalDateTime.now());
dictTagMapper.insert(tag);
}
}
/**
* 根据类型获取某用户的标签列表
*/
public List<String> getTagsByType(Long userId, String type) {
return dictTagMapper.selectList(
new LambdaQueryWrapper<DictTag>()
.eq(DictTag::getUserId, userId)
.eq(DictTag::getType, type)
.orderByAsc(DictTag::getName)
).stream().map(DictTag::getName).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,81 @@
package com.tracexd.service.record;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tracexd.mapper.RecordMapper;
import com.tracexd.model.dto.RecordQueryDTO;
import com.tracexd.model.entity.Record;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
/**
* 事项记录服务 CRUD 和查询
*/
@Service
public class RecordService {
private final RecordMapper recordMapper;
public RecordService(RecordMapper recordMapper) {
this.recordMapper = recordMapper;
}
public void insert(Record record) {
recordMapper.insert(record);
}
public List<Record> executeSelectSql(String sql) {
return recordMapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<Record>()
.apply("1=1 AND " + extractWhereClause(sql)));
}
public IPage<Record> queryPage(RecordQueryDTO queryDTO, Long userId) {
Page<Record> page = new Page<>(queryDTO.getPage(), queryDTO.getSize());
LambdaQueryWrapper<Record> wrapper = new LambdaQueryWrapper<Record>()
.eq(Record::getUserId, userId);
if (queryDTO.getPersons() != null && !queryDTO.getPersons().isEmpty())
wrapper.in(Record::getPerson, queryDTO.getPersons());
if (queryDTO.getLocations() != null && !queryDTO.getLocations().isEmpty())
wrapper.in(Record::getLocation, queryDTO.getLocations());
if (queryDTO.getCategories() != null && !queryDTO.getCategories().isEmpty())
wrapper.in(Record::getCategory, queryDTO.getCategories());
if (queryDTO.getStartDate() != null)
wrapper.ge(Record::getTime, queryDTO.getStartDate().atStartOfDay());
if (queryDTO.getEndDate() != null)
wrapper.le(Record::getTime, queryDTO.getEndDate().plusDays(1).atStartOfDay());
wrapper.orderByDesc(Record::getTime);
return recordMapper.selectPage(page, wrapper);
}
public BigDecimal sumAmount(RecordQueryDTO queryDTO, Long userId) {
LambdaQueryWrapper<Record> wrapper = new LambdaQueryWrapper<Record>()
.eq(Record::getUserId, userId);
if (queryDTO.getPersons() != null && !queryDTO.getPersons().isEmpty())
wrapper.in(Record::getPerson, queryDTO.getPersons());
if (queryDTO.getLocations() != null && !queryDTO.getLocations().isEmpty())
wrapper.in(Record::getLocation, queryDTO.getLocations());
if (queryDTO.getCategories() != null && !queryDTO.getCategories().isEmpty())
wrapper.in(Record::getCategory, queryDTO.getCategories());
if (queryDTO.getStartDate() != null)
wrapper.ge(Record::getTime, queryDTO.getStartDate().atStartOfDay());
if (queryDTO.getEndDate() != null)
wrapper.le(Record::getTime, queryDTO.getEndDate().plusDays(1).atStartOfDay());
List<Record> records = recordMapper.selectList(wrapper);
return records.stream()
.map(r -> r.getAmount() != null ? r.getAmount() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
private String extractWhereClause(String sql) {
int whereIndex = sql.toUpperCase().indexOf("WHERE");
if (whereIndex == -1) return "1=1";
return sql.substring(whereIndex + 5).trim().replaceAll(";$", "");
}
}

View File

@ -0,0 +1,102 @@
package com.tracexd.service.sql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* SQL 语句合法性校验器
*/
@Component
public class SqlValidator {
private static final Logger log = LoggerFactory.getLogger(SqlValidator.class);
private static final List<String> DANGEROUS_KEYWORDS = Arrays.asList(
"DROP", "DELETE", "UPDATE", "ALTER", "TRUNCATE", "CREATE",
"INSERT INTO", "REPLACE", "GRANT", "REVOKE", "EXEC", "EXECUTE",
"CALL", "LOAD_FILE", "INTO OUTFILE", "INTO DUMPFILE"
);
private static final Pattern INSERT_PATTERN = Pattern.compile(
"^\\s*INSERT\\s+INTO\\s+record\\s*\\(.*\\)\\s*VALUES\\s*\\(.*\\)\\s*;?\\s*$",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL
);
private static final Pattern SELECT_PATTERN = Pattern.compile(
"^\\s*SELECT\\s+.*\\s+FROM\\s+record.*$",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL
);
private static final int MAX_RETRY_COUNT = 5;
/**
* 校验 INSERT SQL 的合法性
*
* @param sql 待校验的 SQL
* @return 校验通过返回 true
*/
public boolean validateInsert(String sql) {
if (sql == null || sql.isBlank()) {
log.warn("SQL validation failed: SQL is empty");
return false;
}
String upperSql = sql.toUpperCase();
if (!INSERT_PATTERN.matcher(sql).matches()) {
log.warn("SQL validation failed: not a valid INSERT INTO record: {}", sql);
return false;
}
for (String keyword : DANGEROUS_KEYWORDS) {
// Skip the expected INSERT INTO itself
if ("INSERT INTO".equals(keyword)) continue;
if (upperSql.contains(keyword)) {
log.warn("SQL validation failed: contains dangerous keyword '{}': {}", keyword, sql);
return false;
}
}
return true;
}
/**
* 校验 SELECT SQL 的合法性
*
* @param sql 待校验的 SQL
* @return 校验通过返回 true
*/
public boolean validateSelect(String sql) {
if (sql == null || sql.isBlank()) {
return false;
}
String upperSql = sql.toUpperCase();
if (!SELECT_PATTERN.matcher(sql).matches()) {
log.warn("SQL validation failed: not a valid SELECT FROM record: {}", sql);
return false;
}
for (String keyword : DANGEROUS_KEYWORDS) {
if (upperSql.contains(keyword)) {
log.warn("SQL validation failed: contains dangerous keyword '{}': {}", keyword, sql);
return false;
}
}
return true;
}
/**
* 获取最大重试次数
*/
public int getMaxRetryCount() {
return MAX_RETRY_COUNT;
}
}

View File

@ -0,0 +1,238 @@
package com.tracexd.service.voice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tracexd.common.exception.BusinessException;
import com.tracexd.enums.IntentType;
import com.tracexd.model.dto.VoiceProcessResult;
import com.tracexd.model.entity.Record;
import com.tracexd.service.ai.AiService;
import com.tracexd.service.ai.AiService.IntentResult;
import com.tracexd.service.asr.AsrService;
import com.tracexd.service.record.DictTagService;
import com.tracexd.service.record.RecordService;
import com.tracexd.service.sql.SqlValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* 语音处理编排服务 ASR AI SQL执行 / 查询汇总
*/
@Service
public class VoiceProcessService {
private static final Logger log = LoggerFactory.getLogger(VoiceProcessService.class);
private final AsrService asrService;
private final AiService aiService;
private final SqlValidator sqlValidator;
private final RecordService recordService;
private final DictTagService dictTagService;
private final JdbcTemplate jdbcTemplate;
private final ObjectMapper objectMapper;
public VoiceProcessService(AsrService asrService, AiService aiService,
SqlValidator sqlValidator, RecordService recordService,
DictTagService dictTagService, JdbcTemplate jdbcTemplate,
ObjectMapper objectMapper) {
this.asrService = asrService;
this.aiService = aiService;
this.sqlValidator = sqlValidator;
this.recordService = recordService;
this.dictTagService = dictTagService;
this.jdbcTemplate = jdbcTemplate;
this.objectMapper = objectMapper;
}
/**
* 异步处理语音录入/查询
*/
@Async("voiceTaskExecutor")
public CompletableFuture<VoiceProcessResult> processVoice(byte[] audioData, String mimeType, Long userId) {
try {
String asrText = asrService.recognize(audioData, mimeType);
// ASR no content -> skip LLM
if (asrText == null || asrText.isBlank()) {
log.warn("ASR returned empty result, skipping LLM call");
VoiceProcessResult result = new VoiceProcessResult();
result.setSuccess(false);
result.setReply("未识别到语音内容");
return CompletableFuture.completedFuture(result);
}
return CompletableFuture.completedFuture(processText(asrText, userId));
} catch (Exception e) {
log.error("Voice process failed", e);
VoiceProcessResult result = new VoiceProcessResult();
result.setSuccess(false);
result.setReply("处理失败:" + e.getMessage());
return CompletableFuture.completedFuture(result);
}
}
public VoiceProcessResult processText(String userText, Long userId) {
log.info("Processing text: {}", userText);
IntentResult intentResult = aiService.analyzeIntent(userText);
VoiceProcessResult result = new VoiceProcessResult();
result.setAsrText(userText);
result.setIntent(intentResult.intent());
result.setSql(intentResult.sql());
switch (intentResult.intent()) {
case RECORD -> handleRecord(result, intentResult.sql(), userId);
case QUERY -> handleQuery(result, intentResult.sql(), userText);
case OTHER -> result.setReply(intentResult.reply());
}
result.setSuccess(true);
return result;
}
private void handleRecord(VoiceProcessResult result, String sql, Long userId) {
for (int i = 0; i < sqlValidator.getMaxRetryCount(); i++) {
if (!sqlValidator.validateInsert(sql)) {
log.warn("Insert SQL invalid, retry {}/{}", i + 1, sqlValidator.getMaxRetryCount());
IntentResult retry = aiService.analyzeIntent("请重新生成合法的INSERT SQL语句" + result.getAsrText());
sql = retry.sql();
result.setSql(sql);
continue;
}
try {
String modifiedSql = injectUserId(sql, userId);
jdbcTemplate.update(modifiedSql);
syncDictTagsFromSql(sql, userId);
String category = extractCategoryFromSql(sql);
result.setRecordTip("已录入" + category + "事项");
log.info("Record inserted successfully: {}", sql);
return;
} catch (Exception e) {
log.warn("Insert SQL execution failed, retry {}/{}: {}", i + 1, sqlValidator.getMaxRetryCount(), e.getMessage());
IntentResult retry = aiService.analyzeIntent("请重新生成合法的INSERT SQL语句之前错误" + e.getMessage() + ",原文:" + result.getAsrText());
sql = retry.sql();
result.setSql(sql);
}
}
result.setSuccess(false);
result.setReply("录入失败SQL多次生成仍不合法");
}
private void handleQuery(VoiceProcessResult result, String sql, String userText) {
if (!sqlValidator.validateSelect(sql)) {
result.setSuccess(false);
result.setReply("查询语句不合法");
return;
}
try {
List<Map<String, Object>> queryData = jdbcTemplate.queryForList(sql);
result.setQueryData(queryData);
String queryResultJson;
try {
queryResultJson = objectMapper.writeValueAsString(queryData);
} catch (JsonProcessingException e) {
queryResultJson = queryData.toString();
}
String summary = aiService.summarizeQueryResult(userText, queryResultJson);
result.setReply(summary);
log.info("Query executed, {} rows returned", queryData.size());
} catch (Exception e) {
log.error("Query execution failed", e);
result.setSuccess(false);
result.setReply("查询执行失败:" + e.getMessage());
}
}
private void syncDictTagsFromSql(String sql, Long userId) {
try {
String upperSql = sql.toUpperCase();
int valuesIdx = upperSql.indexOf("VALUES");
if (valuesIdx == -1) return;
String valuesPart = sql.substring(valuesIdx + 6).trim();
valuesPart = valuesPart.replaceAll("^\\(", "").replaceAll("\\)\\s*;?$", "");
String[] parts = splitValues(valuesPart);
if (parts.length >= 6) {
String person = cleanValue(parts[0]);
String location = cleanValue(parts[2]);
String category = cleanValue(parts[5]);
if (person != null && !person.isBlank() && !"".equals(person))
dictTagService.ensureTagExists(userId, "person", person);
if (location != null && !location.isBlank())
dictTagService.ensureTagExists(userId, "location", location);
if (category != null && !category.isBlank())
dictTagService.ensureTagExists(userId, "category", category);
}
} catch (Exception e) {
log.warn("Failed to sync dict tags from SQL: {}", e.getMessage());
}
}
private String injectUserId(String sql, Long userId) {
String upper = sql.toUpperCase();
int valuesIdx = upper.indexOf("VALUES");
if (valuesIdx == -1) return sql;
String beforeValues = sql.substring(0, valuesIdx);
String afterValues = sql.substring(valuesIdx);
int parenOpen = beforeValues.indexOf('(');
int parenClose = beforeValues.lastIndexOf(')');
if (parenOpen == -1 || parenClose == -1) return sql;
String columns = beforeValues.substring(parenOpen + 1, parenClose).trim();
String modifiedColumns = columns + ", user_id";
int valParenOpen = afterValues.indexOf('(');
int valParenClose = afterValues.lastIndexOf(')');
if (valParenOpen == -1 || valParenClose == -1) return sql;
String values = afterValues.substring(valParenOpen + 1, valParenClose).trim();
String modifiedValues = values + ", " + userId;
return beforeValues.substring(0, parenOpen + 1) + modifiedColumns + ")" +
afterValues.substring(0, valParenOpen + 1) + modifiedValues + ")" +
afterValues.substring(valParenClose + 1);
}
private String extractCategoryFromSql(String sql) {
try {
int valuesIdx = sql.toUpperCase().indexOf("VALUES");
if (valuesIdx == -1) return "事项";
String valuesPart = sql.substring(valuesIdx + 6).trim();
valuesPart = valuesPart.replaceAll("^\\(", "").replaceAll("\\)\\s*;?$", "");
String[] parts = splitValues(valuesPart);
if (parts.length >= 6) return cleanValue(parts[5]);
} catch (Exception e) {
log.warn("Failed to extract category", e);
}
return "事项";
}
private String[] splitValues(String valuesPart) {
List<String> result = new java.util.ArrayList<>();
boolean inQuote = false;
char quoteChar = '\'';
StringBuilder current = new StringBuilder();
for (char c : valuesPart.toCharArray()) {
if (inQuote) {
current.append(c);
if (c == quoteChar) inQuote = false;
} else if (c == '\'' || c == '"') {
inQuote = true;
quoteChar = c;
current.append(c);
} else if (c == ',') {
result.add(current.toString());
current = new StringBuilder();
} else {
current.append(c);
}
}
result.add(current.toString());
return result.toArray(String[]::new);
}
private String cleanValue(String value) {
return value.trim().replaceAll("^['\"]", "").replaceAll("['\"]$", "").trim();
}
}

View File

@ -0,0 +1,18 @@
spring:
datasource:
url: jdbc:h2:mem:tracexd;DB_CLOSE_DELAY=-1;MODE=MYSQL
username: sa
password:
driver-class-name: org.h2.Driver
sql:
init:
mode: always
schema-locations: classpath:init-schema.sql
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
com.tracexd: DEBUG

View File

@ -0,0 +1,59 @@
server:
port: 8081
servlet:
session:
timeout: 30d
spring:
main:
banner-mode: off
datasource:
url: jdbc:mysql://192.168.2.5:3306/tracexd?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML
cache: true
servlet:
multipart:
max-file-size: 10MB
security:
user:
name: admin
password: admin123
mybatis-plus:
global-config:
banner: false
configuration:
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
logging:
level:
com.tracexd: INFO
org.springframework: WARN
---
spring:
config:
activate:
on-profile: dev
thymeleaf:
cache: false
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
---
tracexd:
deepseek:
api-key: sk-732e5f5f1f07454492022401f0a2bf40
base-url: https://api.deepseek.com
model: deepseek-v4-pro
mimo:
api-key: sk-cqttr117tzllfolonxztu4qa5208liota2llfut458ixx2m1
base-url: https://api.xiaomimimo.com/v1
model: mimo-v2.5-asr

View File

@ -0,0 +1,37 @@
CREATE TABLE IF NOT EXISTS record (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
person VARCHAR(50) NOT NULL DEFAULT '',
time DATETIME NOT NULL,
location VARCHAR(200),
content VARCHAR(1000),
amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
category VARCHAR(20),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS dict_tag (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
type VARCHAR(20) NOT NULL,
name VARCHAR(200) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_type_name (user_id, type, name)
);
CREATE TABLE IF NOT EXISTS sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
display_name VARCHAR(50) NOT NULL DEFAULT '',
enabled TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_username (username)
);
CREATE TABLE IF NOT EXISTS persistent_logins (
username VARCHAR(64) NOT NULL,
series VARCHAR(64) PRIMARY KEY,
token VARCHAR(64) NOT NULL,
last_used TIMESTAMP NOT NULL
);

View File

@ -0,0 +1,119 @@
/* ===== Reset & Base ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: #f5f5f7; color: #1d1d1f; min-height: 100vh; }
a { color: #007aff; text-decoration: none; }
a:hover { text-decoration: underline; }
.app-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 20px; background: #fff; border-bottom: 1px solid #e5e5e7; position: sticky; top: 0; z-index: 100; }
.header-title { font-size: 18px; font-weight: 600; }
.header-link { font-size: 14px; color: #007aff; }
.header-nav { display: flex; align-items: center; gap: 12px; }
.header-logout { font-size: 14px; color: #999; cursor: pointer; }
.header-logout:hover { color: #ff3b30; }
.main-content { max-width: 960px; margin: 0 auto; padding: 16px; min-height: calc(100vh - 52px); padding-bottom: 100px; }
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%); padding: 10px 24px; border-radius: 8px; font-size: 14px; z-index: 999; transition: opacity 0.3s; text-align: center; max-width: 90%; white-space: nowrap; }
.toast.hidden { opacity: 0; pointer-events: none; }
.toast.show { opacity: 1; }
.toast-success { background: #34c759; color: #fff; }
.toast-error { background: #ff3b30; color: #fff; }
.toast-info { background: #007aff; color: #fff; }
.mic-btn { position: fixed; bottom: 40px; left: 50%; margin-left: -40px; width: 80px; height: 80px; border-radius: 50%; background: #007aff; border: none; cursor: pointer; box-shadow: 0 4px 16px rgba(0,122,255,0.4); display: flex; align-items: center; justify-content: center; transition: transform 0.15s, box-shadow 0.15s, background 0.15s; z-index: 200; touch-action: none; }
.mic-btn:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(0,122,255,0.5); }
.mic-btn:active, .mic-btn.recording { transform: scale(0.95); background: #ff3b30; box-shadow: 0 4px 16px rgba(255,59,48,0.4); }
.mic-icon { width: 38px; height: 38px; color: #fff; }
.result-area { margin-bottom: 16px; }
.query-result-card { background: #fff; border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; }
.query-table { width: 100%; border-collapse: collapse; margin-bottom: 12px; }
.query-table th, .query-table td { padding: 8px 12px; text-align: left; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
.query-table th { font-weight: 600; color: #666; background: #fafafa; }
.ai-summary { padding: 8px 0; font-size: 14px; line-height: 1.6; color: #333; }
.section-title { font-size: 16px; font-weight: 600; margin-bottom: 12px; color: #333; }
.empty-hint { color: #999; font-size: 14px; text-align: center; padding: 32px 0; }
.recent-list { display: flex; flex-direction: column; gap: 8px; }
.record-item { display: flex; align-items: center; gap: 12px; background: #fff; padding: 12px 16px; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #e5e5e7; font-size: 14px; }
.rec-time { color: #666; min-width: 50px; font-size: 13px; white-space: nowrap; }
.rec-person { color: #007aff; font-weight: 500; min-width: 36px; }
.rec-content { flex: 1; color: #333; }
.rec-amount { font-weight: 600; color: #333; min-width: 60px; text-align: right; white-space: nowrap; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; color: #fff; white-space: nowrap; }
.badge-default { background: #666; }
.card-default { background: transparent; }
.card-c1 { background: rgba(255, 149, 0, 0.08); }
.card-c2 { background: rgba(0, 122, 255, 0.08); }
.card-c3 { background: rgba(255, 45, 85, 0.08); }
.card-c4 { background: rgba(175, 82, 222, 0.08); }
.card-c5 { background: rgba(52, 199, 89, 0.08); }
.card-c6 { background: rgba(88, 86, 214, 0.08); }
.card-c7 { background: rgba(255, 149, 0, 0.08); }
.card-c8 { background: rgba(0, 122, 255, 0.08); }
.card-c9 { background: rgba(255, 45, 85, 0.08); }
.card-c10 { background: rgba(175, 82, 222, 0.08); }
.card-c11 { background: rgba(52, 199, 89, 0.08); }
.card-c12 { background: rgba(88, 86, 214, 0.08); }
.badge-c1 { background: #ff9500; }
.badge-c2 { background: #007aff; }
.badge-c3 { background: #ff2d55; }
.badge-c4 { background: #af52de; }
.badge-c5 { background: #34c759; }
.badge-c6 { background: #5856d6; }
.badge-c7 { background: #ff9500; }
.badge-c8 { background: #007aff; }
.badge-c9 { background: #ff2d55; }
.badge-c10 { background: #af52de; }
.badge-c11 { background: #34c759; }
.badge-c12 { background: #5856d6; }
.login-body { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: linear-gradient(135deg, #f5f5f7 0%, #e8e8ed 100%); }
.login-container { background: #fff; padding: 40px; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.08); width: 360px; max-width: 90%; }
.login-title { text-align: center; font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #1d1d1f; }
.login-subtitle { text-align: center; font-size: 14px; color: #999; margin-bottom: 24px; }
.login-error { background: #fff2f2; color: #ff3b30; padding: 8px 12px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; }
.login-info { background: #e8f4ff; color: #007aff; padding: 8px 12px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; color: #555; }
.form-group input { width: 100%; padding: 10px 12px; border: 1px solid #d1d1d6; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; }
.form-group input:focus { border-color: #007aff; box-shadow: 0 0 0 3px rgba(0,122,255,0.1); }
.form-remember { margin-bottom: 20px; font-size: 13px; color: #666; }
.form-remember input { margin-right: 6px; }
.login-btn { width: 100%; padding: 12px; background: #007aff; color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.2s; }
.login-btn:hover { background: #0056cc; }
.filter-section { background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; margin-bottom: 16px; }
.filter-section.hidden { display: none; }
.filter-group { margin-bottom: 16px; }
.filter-group:last-of-type { margin-bottom: 12px; }
.filter-label { display: block; font-size: 13px; font-weight: 600; color: #666; margin-bottom: 8px; }
.filter-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-btn { display: inline-block; padding: 6px 14px; border-radius: 6px; border: 1px solid #d1d1d6; background: #fff; font-size: 13px; cursor: pointer; transition: all 0.15s; user-select: none; color: #333; }
.tag-btn:hover { border-color: #007aff; color: #007aff; }
.tag-btn.active { background: #007aff; color: #fff; border-color: #007aff; }
.filter-date-range { display: flex; align-items: center; gap: 8px; }
.date-input { padding: 6px 12px; border: 1px solid #d1d1d6; border-radius: 6px; font-size: 13px; outline: none; }
.date-input:focus { border-color: #007aff; }
.date-sep { color: #999; }
.filter-actions { display: flex; justify-content: space-between; align-items: center; padding-top: 8px; }
.btn { padding: 8px 24px; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.15s; border: none; }
.btn-primary { background: #007aff; color: #fff; }
.btn-primary:hover { background: #0056cc; }
.btn-secondary { background: #f5f5f7; color: #333; border: 1px solid #d1d1d6; }
.btn-secondary:hover { background: #e8e8ed; }
.btn-link { background: none; color: #007aff; padding: 4px 8px; font-size: 13px; }
.btn-link:hover { text-decoration: underline; }
.btn-page { padding: 6px 14px; font-size: 13px; background: #fff; border: 1px solid #d1d1d6; border-radius: 6px; color: #333; }
.btn-page:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-page:hover:not(:disabled) { border-color: #007aff; color: #007aff; }
.filter-summary { background: #fff; border-radius: 10px; padding: 12px 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; margin-bottom: 16px; font-size: 13px; color: #666; display: flex; align-items: center; gap: 12px; }
.summary-bar { background: #e8f4ff; border-radius: 8px; padding: 10px 16px; margin-bottom: 12px; font-size: 14px; color: #007aff; }
.summary-bar strong { font-size: 16px; }
.table-wrapper { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #e5e5e7; }
.data-table th, .data-table td { padding: 10px 14px; text-align: left; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
.data-table th { background: #fafafa; font-weight: 600; color: #666; white-space: nowrap; }
.data-table tbody tr:hover { background: #f8f8fa; }
.empty-cell { text-align: center; color: #999; padding: 32px 0 !important; }
.col-time { width: 180px; white-space: nowrap; }
.col-person { width: 80px; }
.col-location { min-width: 120px; max-width: 200px; }
.col-content { min-width: 160px; max-width: 400px; }
.col-amount { width: 100px; text-align: right; }
.col-amount-cell { text-align: right; font-weight: 600; }
.col-category { width: 80px; }
.pagination { display: flex; align-items: center; justify-content: center; gap: 16px; margin-top: 16px; font-size: 13px; color: #666; }
@media (max-width: 640px) { .main-content { padding: 12px; padding-bottom: 100px; } .mic-btn { bottom: 24px; left: 50%; margin-left: -34px; width: 68px; height: 68px; } .mic-icon { width: 32px; height: 32px; } .data-table { font-size: 12px; } .data-table th, .data-table td { padding: 8px 10px; } .col-time { width: 140px; } .col-location { max-width: 120px; } .col-content { max-width: 200px; } }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,63 @@
/**
* TraceXD 前端通用工具函数
*/
(function(global) {
/**
* 格式化时间显示
* 当天 HH:mm
* 当年非当天 M-d HH:mm
* 非当年 yy-M-d HH
*
* @param {string} isoString ISO 日期时间字符串
* @returns {string} 格式化后的时间文本
*/
global.formatTime = function(isoString) {
if (!isoString) return '';
var d = new Date(isoString);
var pad = function(n) { return String(n).padStart(2, '0'); };
return (d.getMonth() + 1) + '-' + d.getDate() + ' ' +
pad(d.getHours()) + ':' + pad(d.getMinutes());
};
/**
* ??????? badge CSS ??
* ??????????????????????????
*/
global.badgeClass = function(category) {
if (!category) return 'badge-default';
var fixed = {
'餐饮': 'badge-c1',
'饮食': 'badge-c1',
'交通': 'badge-c2',
'出行': 'badge-c2',
'购物': 'badge-c3',
'娱乐': 'badge-c4',
'医疗': 'badge-c5',
'健康': 'badge-c5',
'教育': 'badge-c6',
'学习': 'badge-c6',
'养老': 'badge-c7',
'社交': 'badge-c8',
'旅游': 'badge-c9',
'居家': 'badge-c10'
};
if (fixed[category]) return fixed[category];
// hash-based fallback
var hash = 0;
for (var i = 0; i < category.length; i++) {
hash = ((hash << 5) - hash) + category.charCodeAt(i);
hash |= 0;
}
var idx = (Math.abs(hash) % 12) + 1;
return 'badge-c' + idx;
};
global.logout = function() {
fetch('/logout', { method: 'POST' }).then(function() {
window.location.href = '/login?logout';
});
};
})(window);

View File

@ -0,0 +1,271 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TraceXD - 浏览记录</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header class="app-header">
<a href="/" class="header-link">首页</a>
<span class="header-title">浏览记录</span>
<a href="#" onclick="logout(); return false;" class="header-logout">退出</a>
</header>
<main class="main-content">
<!-- 筛选项区域 -->
<div id="filter-section" class="filter-section">
<div class="filter-group">
<label class="filter-label">人物</label>
<div id="filter-persons" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">地点</label>
<div id="filter-locations" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">分类</label>
<div id="filter-categories" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">时间</label>
<div class="filter-date-range">
<input type="date" id="filter-start-date" class="date-input">
<span class="date-sep">~</span>
<input type="date" id="filter-end-date" class="date-input">
</div>
</div>
<div class="filter-actions">
<button id="btn-reset" class="btn btn-secondary">重置</button>
<button id="btn-query" class="btn btn-primary">查询</button>
</div>
</div>
<!-- 折叠后的筛选项摘要 -->
<div id="filter-summary" class="filter-summary hidden">
<span id="filter-summary-text"></span>
<button id="btn-expand" class="btn btn-link">展开筛选项</button>
</div>
<!-- 金额汇总 -->
<div id="summary-bar" class="summary-bar hidden">
金额汇总: <strong id="total-amount">¥0.00</strong>
</div>
<!-- 数据表格 -->
<div class="table-wrapper">
<table class="data-table" id="data-table">
<thead>
<tr>
<th class="col-time">时间</th>
<th class="col-person">人物</th>
<th class="col-location">地点</th>
<th class="col-content">事项内容</th>
<th class="col-amount">金额</th>
<th class="col-category">分类</th>
</tr>
</thead>
<tbody id="table-body">
<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div id="pagination" class="pagination hidden">
<button id="btn-prev" class="btn btn-page">&lt; 上一页</button>
<span id="page-info"></span>
<button id="btn-next" class="btn btn-page">下一页 &gt;</button>
</div>
</main>
</div>
<script src="/js/app.js"></script>
<script>
(function() {
var currentPage = 1;
var pageSize = 20;
var totalPages = 0;
var filterCollapsed = false;
var selectedPersons = [];
var selectedLocations = [];
var selectedCategories = [];
function init() {
loadFilters();
document.getElementById('btn-query').addEventListener('click', function() {
doQuery(1);
});
document.getElementById('btn-reset').addEventListener('click', function() {
resetFilters();
});
document.getElementById('btn-expand').addEventListener('click', function() {
toggleFilterSection(false);
});
document.getElementById('btn-prev').addEventListener('click', function() {
if (currentPage > 1) doQuery(currentPage - 1);
});
document.getElementById('btn-next').addEventListener('click', function() {
if (currentPage < totalPages) doQuery(currentPage + 1);
});
}
function loadFilters() {
fetch('/api/records/filters')
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
var data = resp.data;
renderFilterTags('filter-persons', data.persons || [], 'person');
renderFilterTags('filter-locations', data.locations || [], 'location');
renderFilterTags('filter-categories', data.categories || [], 'category');
});
}
function renderFilterTags(containerId, items, type) {
var container = document.getElementById(containerId);
container.innerHTML = '';
items.forEach(function(item) {
var tag = document.createElement('span');
tag.className = 'tag-btn';
tag.textContent = item;
tag.dataset.type = type;
tag.dataset.value = item;
tag.addEventListener('click', function() {
this.classList.toggle('active');
});
container.appendChild(tag);
});
}
function getSelectedTags(type) {
var tags = document.querySelectorAll('.tag-btn[data-type="' + type + '"].active');
return Array.from(tags).map(function(t) { return t.dataset.value; });
}
function getQueryParams() {
selectedPersons = getSelectedTags('person');
selectedLocations = getSelectedTags('location');
selectedCategories = getSelectedTags('category');
var startDate = document.getElementById('filter-start-date').value;
var endDate = document.getElementById('filter-end-date').value;
return {
persons: selectedPersons,
locations: selectedLocations,
categories: selectedCategories,
startDate: startDate,
endDate: endDate
};
}
function buildFilterSummary(params) {
var parts = [];
if (params.persons.length) parts.push('人物(' + params.persons.join(',') + ')');
if (params.locations.length) parts.push('地点(' + params.locations.join(',') + ')');
if (params.categories.length) parts.push('分类(' + params.categories.join(',') + ')');
if (params.startDate || params.endDate) {
parts.push('时间(' + (params.startDate || '不限') + '~' + (params.endDate || '不限') + ')');
}
return parts.join(' | ') || '无条件';
}
function toggleFilterSection(collapsed) {
filterCollapsed = collapsed;
document.getElementById('filter-section').classList.toggle('hidden', collapsed);
document.getElementById('filter-summary').classList.toggle('hidden', !collapsed);
}
function doQuery(page) {
var params = getQueryParams();
var queryStr = '?page=' + page + '&size=' + pageSize;
if (params.persons.length) {
params.persons.forEach(function(p) { queryStr += '&persons=' + encodeURIComponent(p); });
}
if (params.locations.length) {
params.locations.forEach(function(l) { queryStr += '&locations=' + encodeURIComponent(l); });
}
if (params.categories.length) {
params.categories.forEach(function(c) { queryStr += '&categories=' + encodeURIComponent(c); });
}
if (params.startDate) queryStr += '&startDate=' + params.startDate;
if (params.endDate) queryStr += '&endDate=' + params.endDate;
fetch('/api/records' + queryStr)
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
renderTable(resp.data);
toggleFilterSection(true);
var summary = document.getElementById('filter-summary-text');
summary.textContent = '已选: ' + buildFilterSummary(params);
});
}
function renderTable(data) {
if (!data) return;
var records = data.records || [];
var totalAmount = data.totalAmount || 0;
var total = data.total || 0;
currentPage = Number(data.page) || 1;
totalPages = Math.ceil(total / pageSize);
// 更新金额汇总
var summaryBar = document.getElementById('summary-bar');
summaryBar.classList.remove('hidden');
document.getElementById('total-amount').textContent = '¥' + Number(totalAmount).toFixed(2);
// 更新表格
var tbody = document.getElementById('table-body');
if (records.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="empty-cell">暂无数据</td></tr>';
} else {
var html = '';
records.forEach(function(r) {
html += '<tr>' +
'<td>' + formatTime(r.time) + '</td>' +
'<td>' + (r.person || '我') + '</td>' +
'<td>' + (r.location || '-') + '</td>' +
'<td>' + (r.content || '') + '</td>' +
'<td class="col-amount-cell">¥' + (r.amount ? Number(r.amount).toFixed(2) : '0.00') + '</td>' +
'<td><span class="badge badge ' + badgeClass(r.category) + '">' +
(r.category || '-') + '</span></td>' +
'</tr>';
});
tbody.innerHTML = html;
}
// 更新分页
var pagination = document.getElementById('pagination');
if (totalPages > 1) {
pagination.classList.remove('hidden');
document.getElementById('page-info').textContent =
'第 ' + currentPage + ' / ' + totalPages + ' 页(共 ' + total + ' 条)';
document.getElementById('btn-prev').disabled = currentPage <= 1;
document.getElementById('btn-next').disabled = currentPage >= totalPages;
} else {
pagination.classList.add('hidden');
}
}
function resetFilters() {
document.querySelectorAll('.tag-btn').forEach(function(t) { t.classList.remove('active'); });
document.getElementById('filter-start-date').value = '';
document.getElementById('filter-end-date').value = '';
document.getElementById('table-body').innerHTML =
'<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>';
document.getElementById('summary-bar').classList.add('hidden');
document.getElementById('pagination').classList.add('hidden');
toggleFilterSection(false);
}
init();
})();
</script>
</body>
</html>

View File

@ -0,0 +1,329 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>TraceXD - 首页</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header class="app-header">
<span class="header-title">TraceXD</span>
<nav class="header-nav">
<a href="/browse" class="header-link">浏览记录</a>
<a href="#" onclick="logout(); return false;" class="header-logout">退出</a>
</nav>
</header>
<main class="main-content">
<!-- 结果展示区 -->
<div id="result-area" class="result-area"></div>
<!-- Toast 通知 -->
<div id="toast" class="toast hidden"></div>
<!-- 最近事项 -->
<div class="recent-section">
<h3 class="section-title">最近事项</h3>
<div id="recent-list" class="recent-list">
<p class="empty-hint">暂无事项记录,点击右下角麦克风开始录入</p>
</div>
</div>
</main>
<!-- 麦克风按钮 -->
<button id="mic-btn" class="mic-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mic-icon">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
</button>
</div>
<script src="/js/app.js"></script>
<script>
(function() {
const micBtn = document.getElementById('mic-btn');
const resultArea = document.getElementById('result-area');
const toast = document.getElementById('toast');
const recentList = document.getElementById('recent-list');
let isRecording = false;
let stream = null;
// Web Audio API WAV recording variables
let audioCtx = null;
let mediaStreamSrc = null;
let procNode = null;
let pcmChunks = [];
let recordedSampleRate = 48000;
// 时间格式化
function formatTime(isoString) {
if (!isoString) return '';
const d = new Date(isoString);
const pad = n => String(n).padStart(2, '0');
return (d.getMonth() + 1) + '-' + d.getDate() + ' ' +
pad(d.getHours()) + ':' + pad(d.getMinutes());
}
// 显示 Toast
function showToast(message, type) {
toast.textContent = message;
toast.className = 'toast toast-' + (type || 'success') + ' show';
setTimeout(function() {
toast.className = 'toast hidden';
}, 3000);
}
// 展示查询结果
function showQueryResult(data) {
var html = '<div class="query-result-card">';
if (data.queryData && data.queryData.length > 0) {
html += '<table class="query-table"><thead><tr>' +
'<th>时间</th><th>人物</th><th>内容</th><th>金额</th>' +
'</tr></thead><tbody>';
data.queryData.forEach(function(row) {
html += '<tr class="' + cardClass(row.category) + '"><td>' + formatTime(row.time) + '</td>' +
'<td>' + (row.person || '') + '</td>' +
'<td>' + (row.content || '') + '</td>' +
'<td>' + (row.amount || '0') + '</td></tr>';
});
html += '</tbody></table>';
}
if (data.reply) {
html += '<div class="ai-summary">' + data.reply + '</div>';
}
html += '</div>';
resultArea.innerHTML = html;
}
// 展示AI直接回复其他意图
function showAiReply(text) {
var html = '<div class="query-result-card"><div class="ai-summary">' +
text + '</div></div>';
resultArea.innerHTML = html;
}
// 开始录音
function startRecording() {
pcmChunks = [];
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function(mediaStream) {
stream = mediaStream;
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
recordedSampleRate = audioCtx.sampleRate;
mediaStreamSrc = audioCtx.createMediaStreamSource(mediaStream);
procNode = audioCtx.createScriptProcessor(4096, 1, 1);
procNode.onaudioprocess = function(event) {
var channelData = event.inputBuffer.getChannelData(0);
var buf = new Int16Array(channelData.length);
for (var i = 0; i < channelData.length; i++) {
var s = Math.max(-1, Math.min(1, channelData[i]));
buf[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
}
pcmChunks.push(buf);
};
mediaStreamSrc.connect(procNode);
procNode.connect(audioCtx.destination);
isRecording = true;
micBtn.classList.add('recording');
})
.catch(function(err) {
showToast('无法访问麦克风 ' + err.message, 'error');
});
}
function stopRecording() {
if (!isRecording) return;
isRecording = false;
micBtn.classList.remove('recording');
if (procNode) {
procNode.disconnect();
procNode = null;
}
if (mediaStreamSrc) {
mediaStreamSrc.disconnect();
mediaStreamSrc = null;
}
if (audioCtx) {
audioCtx.close();
audioCtx = null;
}
if (stream) {
stream.getTracks().forEach(function(t) { t.stop(); });
stream = null;
}
if (pcmChunks.length === 0) return;
var totalLen = pcmChunks.reduce(function(sum, chunk) { return sum + chunk.length; }, 0);
var allSamples = new Int16Array(totalLen);
var offset = 0;
pcmChunks.forEach(function(chunk) {
allSamples.set(chunk, offset);
offset += chunk.length;
});
var wavBlob = encodeWav(allSamples, recordedSampleRate);
sendAudio(wavBlob);
}
function encodeWav(samples, sampleRate) {
var numChannels = 1;
var bitsPerSample = 16;
var bytesPerSample = bitsPerSample / 8;
var byteRate = sampleRate * numChannels * bytesPerSample;
var blockAlign = numChannels * bytesPerSample;
var dataSize = samples.length * bytesPerSample;
var buf = new ArrayBuffer(44 + dataSize);
var dv = new DataView(buf);
function writeStr(offset, str) {
for (var i = 0; i < str.length; i++) {
dv.setUint8(offset + i, str.charCodeAt(i));
}
}
writeStr(0, 'RIFF');
dv.setUint32(4, 36 + dataSize, true);
writeStr(8, 'WAVE');
writeStr(12, 'fmt ');
dv.setUint32(16, 16, true);
dv.setUint16(20, 1, true);
dv.setUint16(22, numChannels, true);
dv.setUint32(24, sampleRate, true);
dv.setUint32(28, byteRate, true);
dv.setUint16(32, blockAlign, true);
dv.setUint16(34, bitsPerSample, true);
writeStr(36, 'data');
dv.setUint32(40, dataSize, true);
var ptr = 44;
for (var i = 0; i < samples.length; i++) {
dv.setInt16(ptr, samples[i], true);
ptr += 2;
}
return new Blob([buf], { type: 'audio/wav' });
}
function cardClass(category) {
if (!category) return 'card-default';
var fixed = {'\u9910\u996e':'card-c1','\u996e\u98df':'card-c1','\u4ea4\u901a':'card-c2','\u51fa\u884c':'card-c2','\u8d2d\u7269':'card-c3','\u5a31\u4e50':'card-c4','\u533b\u7597':'card-c5','\u5065\u5eb7':'card-c5','\u6559\u80b2':'card-c6','\u5b66\u4e60':'card-c6'};
if (fixed[category]) return fixed[category];
var hash = 0;
for (var i = 0; i < category.length; i++) {
hash = ((hash << 5) - hash) + category.charCodeAt(i);
hash |= 0;
}
return 'card-c' + ((Math.abs(hash) % 12) + 1);
}
function sendAudio(blob) {
showToast('处理中...', 'info');
var formData = new FormData();
formData.append('audio', blob, 'recording.wav');
fetch('/api/voice/process', { method: 'POST', body: formData })
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) {
showToast(resp.message, 'error');
return;
}
var taskId = resp.data.taskId;
var es = new EventSource('/api/voice/stream/' + taskId);
es.onmessage = function(event) {
var result = JSON.parse(event.data);
es.close();
handleVoiceResult(result);
};
es.onerror = function() {
es.close();
showToast('处理超时,请重试', 'error');
};
})
.catch(function(err) {
showToast('网络错误: ' + err.message, 'error');
});
}
function handleVoiceResult(result) {
if (result.code !== 200) {
showToast(result.message, 'error');
return;
}
var data = result.data;
if (data.intent === 'RECORD') {
if (data.success && data.recordTip) {
showToast(data.recordTip, 'success');
loadRecentRecords();
} else {
showToast(data.reply || '录入失败', 'error');
}
} else if (data.intent === 'QUERY') {
showQueryResult(data);
showToast('查询完成', 'info');
} else {
showAiReply(data.reply);
}
}
// 加载最近事项
function loadRecentRecords() {
fetch('/api/records?page=1&size=5')
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200 || !resp.data) return;
var records = resp.data.records || [];
if (records.length === 0) {
recentList.innerHTML = '<p class="empty-hint">暂无事项记录</p>';
return;
}
var html = '';
records.forEach(function(r) {
html += '<div class="record-item ' + cardClass(r.category) + '">' +
'<span class="rec-time">' + formatTime(r.time) + '</span>' +
'<span class="rec-person">' + (r.person || '我') + '</span>' +
'<span class="rec-content">' + (r.content || '') + '</span>' +
'<span class="rec-amount">¥' + (r.amount || '0') + '</span>' +
'</div>';
});
recentList.innerHTML = html;
})
.catch(function() {});
}
// 事件绑定
micBtn.addEventListener('mousedown', startRecording);
micBtn.addEventListener('mouseup', stopRecording);
micBtn.addEventListener('mouseleave', stopRecording);
micBtn.addEventListener('touchstart', function(e) {
e.preventDefault();
startRecording();
});
micBtn.addEventListener('touchend', function(e) {
e.preventDefault();
stopRecording();
});
// 初始化加载最近事项
loadRecentRecords();
})();
</script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 - TraceXD</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body class="login-body">
<div class="login-container">
<h1 class="login-title">TraceXD</h1>
<p class="login-subtitle">日常事项记录系统</p>
<form class="login-form" method="post" th:action="@{/login}">
<div th:if="${param.error}" class="login-error">
用户名或密码错误
</div>
<div th:if="${param.logout}" class="login-info">
您已退出登录
</div>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-remember">
<label>
<input type="checkbox" name="remember-me" checked> 记住我30天免登录
</label>
</div>
<button type="submit" class="login-btn">登 录</button>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
package com.tracexd;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 应用启动测试
*/
@SpringBootTest
class TracexdApplicationTests {
@Test
void contextLoads() {
}
}

12
stderr.log Normal file
View File

@ -0,0 +1,12 @@
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
WARNING: Final field _cipher in class org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher has been mutated reflectively by class org.eclipse.sisu.bean.BeanPropertyField in unnamed module @780cb77 (file:/C:/Users/16332/.m2/wrapper/dists/apache-maven-3.9.9-bin/4nf9hui3q3djbarqar9g711ggc/apache-maven-3.9.9/lib/org.eclipse.sisu.inject-0.9.0.M3.jar)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled

45
stdout.log Normal file
View File

@ -0,0 +1,45 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.tracexd:tracexd >-------------------------
[INFO] Building tracexd 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot:3.5.14:run (default-cli) > test-compile @ tracexd >>>
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ tracexd ---
[INFO] Copying 2 resources from src\main\resources to target\classes
[INFO] Copying 6 resources from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.14.1:compile (default-compile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ tracexd ---
[INFO] skip non existing resourceDirectory C:\Work\tracexd\src\test\resources
[INFO]
[INFO] --- compiler:3.14.1:testCompile (default-testCompile) @ tracexd ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] <<< spring-boot:3.5.14:run (default-cli) < test-compile @ tracexd <<<
[INFO]
[INFO]
[INFO] --- spring-boot:3.5.14:run (default-cli) @ tracexd ---
[INFO] Attaching agents: []
2026-06-09T14:15:07.058+08:00 INFO 5764 --- [ main] com.tracexd.TracexdApplication : Starting TracexdApplication using Java 26.0.1 with PID 5764 (C:\Work\tracexd\target\classes started by 16332 in C:\Work\tracexd)
2026-06-09T14:15:07.059+08:00 DEBUG 5764 --- [ main] com.tracexd.TracexdApplication : Running with Spring Boot v3.5.14, Spring v6.2.18
2026-06-09T14:15:07.059+08:00 INFO 5764 --- [ main] com.tracexd.TracexdApplication : The following 1 profile is active: "test"
2026-06-09T14:15:07.660+08:00 INFO 5764 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-06-09T14:15:07.660+08:00 INFO 5764 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.54]
2026-06-09T14:15:07.687+08:00 INFO 5764 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Get /198.18.0.1 network interface
Get network interface info: name:iftype53_32769 (Meta Tunnel)
Initialization Sequence datacenterId:0 workerId:16
2026-06-09T14:15:07.788+08:00 INFO 5764 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-06-09T14:15:07.883+08:00 INFO 5764 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:tracexd user=SA
2026-06-09T14:15:07.884+08:00 INFO 5764 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-06-09T14:15:08.298+08:00 INFO 5764 --- [ main] m.e.s.MybatisPlusApplicationContextAware : Register ApplicationContext instances org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@e383572
2026-06-09T14:15:08.339+08:00 INFO 5764 --- [ main] com.tracexd.TracexdApplication : Started TracexdApplication in 1.545 seconds (process running for 1.715)
Default admin user created (admin/admin123)
2026-06-09T14:15:24.899+08:00 INFO 5764 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-06-09T14:15:24.969+08:00 WARN 5764 --- [nio-8080-exec-1] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt

View File

@ -0,0 +1,18 @@
spring:
datasource:
url: jdbc:h2:mem:tracexd;DB_CLOSE_DELAY=-1;MODE=MYSQL
username: sa
password:
driver-class-name: org.h2.Driver
sql:
init:
mode: always
schema-locations: classpath:init-schema.sql
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
com.tracexd: DEBUG

View File

@ -0,0 +1,59 @@
server:
port: 8081
servlet:
session:
timeout: 30d
spring:
main:
banner-mode: off
datasource:
url: jdbc:mysql://192.168.2.5:3306/tracexd?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML
cache: true
servlet:
multipart:
max-file-size: 10MB
security:
user:
name: admin
password: admin123
mybatis-plus:
global-config:
banner: false
configuration:
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
logging:
level:
com.tracexd: INFO
org.springframework: WARN
---
spring:
config:
activate:
on-profile: dev
thymeleaf:
cache: false
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
---
tracexd:
deepseek:
api-key: sk-732e5f5f1f07454492022401f0a2bf40
base-url: https://api.deepseek.com
model: deepseek-v4-pro
mimo:
api-key: sk-cqttr117tzllfolonxztu4qa5208liota2llfut458ixx2m1
base-url: https://api.xiaomimimo.com/v1
model: mimo-v2.5-asr

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
CREATE TABLE IF NOT EXISTS record (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
person VARCHAR(50) NOT NULL DEFAULT '',
time DATETIME NOT NULL,
location VARCHAR(200),
content VARCHAR(1000),
amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
category VARCHAR(20),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS dict_tag (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
type VARCHAR(20) NOT NULL,
name VARCHAR(200) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_type_name (user_id, type, name)
);
CREATE TABLE IF NOT EXISTS sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
display_name VARCHAR(50) NOT NULL DEFAULT '',
enabled TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_username (username)
);
CREATE TABLE IF NOT EXISTS persistent_logins (
username VARCHAR(64) NOT NULL,
series VARCHAR(64) PRIMARY KEY,
token VARCHAR(64) NOT NULL,
last_used TIMESTAMP NOT NULL
);

View File

@ -0,0 +1,119 @@
/* ===== Reset & Base ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: #f5f5f7; color: #1d1d1f; min-height: 100vh; }
a { color: #007aff; text-decoration: none; }
a:hover { text-decoration: underline; }
.app-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 20px; background: #fff; border-bottom: 1px solid #e5e5e7; position: sticky; top: 0; z-index: 100; }
.header-title { font-size: 18px; font-weight: 600; }
.header-link { font-size: 14px; color: #007aff; }
.header-nav { display: flex; align-items: center; gap: 12px; }
.header-logout { font-size: 14px; color: #999; cursor: pointer; }
.header-logout:hover { color: #ff3b30; }
.main-content { max-width: 960px; margin: 0 auto; padding: 16px; min-height: calc(100vh - 52px); padding-bottom: 100px; }
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%); padding: 10px 24px; border-radius: 8px; font-size: 14px; z-index: 999; transition: opacity 0.3s; text-align: center; max-width: 90%; white-space: nowrap; }
.toast.hidden { opacity: 0; pointer-events: none; }
.toast.show { opacity: 1; }
.toast-success { background: #34c759; color: #fff; }
.toast-error { background: #ff3b30; color: #fff; }
.toast-info { background: #007aff; color: #fff; }
.mic-btn { position: fixed; bottom: 40px; left: 50%; margin-left: -40px; width: 80px; height: 80px; border-radius: 50%; background: #007aff; border: none; cursor: pointer; box-shadow: 0 4px 16px rgba(0,122,255,0.4); display: flex; align-items: center; justify-content: center; transition: transform 0.15s, box-shadow 0.15s, background 0.15s; z-index: 200; touch-action: none; }
.mic-btn:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(0,122,255,0.5); }
.mic-btn:active, .mic-btn.recording { transform: scale(0.95); background: #ff3b30; box-shadow: 0 4px 16px rgba(255,59,48,0.4); }
.mic-icon { width: 38px; height: 38px; color: #fff; }
.result-area { margin-bottom: 16px; }
.query-result-card { background: #fff; border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; }
.query-table { width: 100%; border-collapse: collapse; margin-bottom: 12px; }
.query-table th, .query-table td { padding: 8px 12px; text-align: left; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
.query-table th { font-weight: 600; color: #666; background: #fafafa; }
.ai-summary { padding: 8px 0; font-size: 14px; line-height: 1.6; color: #333; }
.section-title { font-size: 16px; font-weight: 600; margin-bottom: 12px; color: #333; }
.empty-hint { color: #999; font-size: 14px; text-align: center; padding: 32px 0; }
.recent-list { display: flex; flex-direction: column; gap: 8px; }
.record-item { display: flex; align-items: center; gap: 12px; background: #fff; padding: 12px 16px; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #e5e5e7; font-size: 14px; }
.rec-time { color: #666; min-width: 50px; font-size: 13px; white-space: nowrap; }
.rec-person { color: #007aff; font-weight: 500; min-width: 36px; }
.rec-content { flex: 1; color: #333; }
.rec-amount { font-weight: 600; color: #333; min-width: 60px; text-align: right; white-space: nowrap; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; color: #fff; white-space: nowrap; }
.badge-default { background: #666; }
.card-default { background: transparent; }
.card-c1 { background: rgba(255, 149, 0, 0.08); }
.card-c2 { background: rgba(0, 122, 255, 0.08); }
.card-c3 { background: rgba(255, 45, 85, 0.08); }
.card-c4 { background: rgba(175, 82, 222, 0.08); }
.card-c5 { background: rgba(52, 199, 89, 0.08); }
.card-c6 { background: rgba(88, 86, 214, 0.08); }
.card-c7 { background: rgba(255, 149, 0, 0.08); }
.card-c8 { background: rgba(0, 122, 255, 0.08); }
.card-c9 { background: rgba(255, 45, 85, 0.08); }
.card-c10 { background: rgba(175, 82, 222, 0.08); }
.card-c11 { background: rgba(52, 199, 89, 0.08); }
.card-c12 { background: rgba(88, 86, 214, 0.08); }
.badge-c1 { background: #ff9500; }
.badge-c2 { background: #007aff; }
.badge-c3 { background: #ff2d55; }
.badge-c4 { background: #af52de; }
.badge-c5 { background: #34c759; }
.badge-c6 { background: #5856d6; }
.badge-c7 { background: #ff9500; }
.badge-c8 { background: #007aff; }
.badge-c9 { background: #ff2d55; }
.badge-c10 { background: #af52de; }
.badge-c11 { background: #34c759; }
.badge-c12 { background: #5856d6; }
.login-body { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: linear-gradient(135deg, #f5f5f7 0%, #e8e8ed 100%); }
.login-container { background: #fff; padding: 40px; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.08); width: 360px; max-width: 90%; }
.login-title { text-align: center; font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #1d1d1f; }
.login-subtitle { text-align: center; font-size: 14px; color: #999; margin-bottom: 24px; }
.login-error { background: #fff2f2; color: #ff3b30; padding: 8px 12px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; }
.login-info { background: #e8f4ff; color: #007aff; padding: 8px 12px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; color: #555; }
.form-group input { width: 100%; padding: 10px 12px; border: 1px solid #d1d1d6; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; }
.form-group input:focus { border-color: #007aff; box-shadow: 0 0 0 3px rgba(0,122,255,0.1); }
.form-remember { margin-bottom: 20px; font-size: 13px; color: #666; }
.form-remember input { margin-right: 6px; }
.login-btn { width: 100%; padding: 12px; background: #007aff; color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.2s; }
.login-btn:hover { background: #0056cc; }
.filter-section { background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; margin-bottom: 16px; }
.filter-section.hidden { display: none; }
.filter-group { margin-bottom: 16px; }
.filter-group:last-of-type { margin-bottom: 12px; }
.filter-label { display: block; font-size: 13px; font-weight: 600; color: #666; margin-bottom: 8px; }
.filter-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-btn { display: inline-block; padding: 6px 14px; border-radius: 6px; border: 1px solid #d1d1d6; background: #fff; font-size: 13px; cursor: pointer; transition: all 0.15s; user-select: none; color: #333; }
.tag-btn:hover { border-color: #007aff; color: #007aff; }
.tag-btn.active { background: #007aff; color: #fff; border-color: #007aff; }
.filter-date-range { display: flex; align-items: center; gap: 8px; }
.date-input { padding: 6px 12px; border: 1px solid #d1d1d6; border-radius: 6px; font-size: 13px; outline: none; }
.date-input:focus { border-color: #007aff; }
.date-sep { color: #999; }
.filter-actions { display: flex; justify-content: space-between; align-items: center; padding-top: 8px; }
.btn { padding: 8px 24px; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.15s; border: none; }
.btn-primary { background: #007aff; color: #fff; }
.btn-primary:hover { background: #0056cc; }
.btn-secondary { background: #f5f5f7; color: #333; border: 1px solid #d1d1d6; }
.btn-secondary:hover { background: #e8e8ed; }
.btn-link { background: none; color: #007aff; padding: 4px 8px; font-size: 13px; }
.btn-link:hover { text-decoration: underline; }
.btn-page { padding: 6px 14px; font-size: 13px; background: #fff; border: 1px solid #d1d1d6; border-radius: 6px; color: #333; }
.btn-page:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-page:hover:not(:disabled) { border-color: #007aff; color: #007aff; }
.filter-summary { background: #fff; border-radius: 10px; padding: 12px 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e5e5e7; margin-bottom: 16px; font-size: 13px; color: #666; display: flex; align-items: center; gap: 12px; }
.summary-bar { background: #e8f4ff; border-radius: 8px; padding: 10px 16px; margin-bottom: 12px; font-size: 14px; color: #007aff; }
.summary-bar strong { font-size: 16px; }
.table-wrapper { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #e5e5e7; }
.data-table th, .data-table td { padding: 10px 14px; text-align: left; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
.data-table th { background: #fafafa; font-weight: 600; color: #666; white-space: nowrap; }
.data-table tbody tr:hover { background: #f8f8fa; }
.empty-cell { text-align: center; color: #999; padding: 32px 0 !important; }
.col-time { width: 180px; white-space: nowrap; }
.col-person { width: 80px; }
.col-location { min-width: 120px; max-width: 200px; }
.col-content { min-width: 160px; max-width: 400px; }
.col-amount { width: 100px; text-align: right; }
.col-amount-cell { text-align: right; font-weight: 600; }
.col-category { width: 80px; }
.pagination { display: flex; align-items: center; justify-content: center; gap: 16px; margin-top: 16px; font-size: 13px; color: #666; }
@media (max-width: 640px) { .main-content { padding: 12px; padding-bottom: 100px; } .mic-btn { bottom: 24px; left: 50%; margin-left: -34px; width: 68px; height: 68px; } .mic-icon { width: 32px; height: 32px; } .data-table { font-size: 12px; } .data-table th, .data-table td { padding: 8px 10px; } .col-time { width: 140px; } .col-location { max-width: 120px; } .col-content { max-width: 200px; } }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,63 @@
/**
* TraceXD 前端通用工具函数
*/
(function(global) {
/**
* 格式化时间显示
* 当天 HH:mm
* 当年非当天 M-d HH:mm
* 非当年 yy-M-d HH
*
* @param {string} isoString ISO 日期时间字符串
* @returns {string} 格式化后的时间文本
*/
global.formatTime = function(isoString) {
if (!isoString) return '';
var d = new Date(isoString);
var pad = function(n) { return String(n).padStart(2, '0'); };
return (d.getMonth() + 1) + '-' + d.getDate() + ' ' +
pad(d.getHours()) + ':' + pad(d.getMinutes());
};
/**
* ??????? badge CSS ??
* ??????????????????????????
*/
global.badgeClass = function(category) {
if (!category) return 'badge-default';
var fixed = {
'餐饮': 'badge-c1',
'饮食': 'badge-c1',
'交通': 'badge-c2',
'出行': 'badge-c2',
'购物': 'badge-c3',
'娱乐': 'badge-c4',
'医疗': 'badge-c5',
'健康': 'badge-c5',
'教育': 'badge-c6',
'学习': 'badge-c6',
'养老': 'badge-c7',
'社交': 'badge-c8',
'旅游': 'badge-c9',
'居家': 'badge-c10'
};
if (fixed[category]) return fixed[category];
// hash-based fallback
var hash = 0;
for (var i = 0; i < category.length; i++) {
hash = ((hash << 5) - hash) + category.charCodeAt(i);
hash |= 0;
}
var idx = (Math.abs(hash) % 12) + 1;
return 'badge-c' + idx;
};
global.logout = function() {
fetch('/logout', { method: 'POST' }).then(function() {
window.location.href = '/login?logout';
});
};
})(window);

View File

@ -0,0 +1,271 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TraceXD - 浏览记录</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header class="app-header">
<a href="/" class="header-link">首页</a>
<span class="header-title">浏览记录</span>
<a href="#" onclick="logout(); return false;" class="header-logout">退出</a>
</header>
<main class="main-content">
<!-- 筛选项区域 -->
<div id="filter-section" class="filter-section">
<div class="filter-group">
<label class="filter-label">人物</label>
<div id="filter-persons" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">地点</label>
<div id="filter-locations" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">分类</label>
<div id="filter-categories" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">时间</label>
<div class="filter-date-range">
<input type="date" id="filter-start-date" class="date-input">
<span class="date-sep">~</span>
<input type="date" id="filter-end-date" class="date-input">
</div>
</div>
<div class="filter-actions">
<button id="btn-reset" class="btn btn-secondary">重置</button>
<button id="btn-query" class="btn btn-primary">查询</button>
</div>
</div>
<!-- 折叠后的筛选项摘要 -->
<div id="filter-summary" class="filter-summary hidden">
<span id="filter-summary-text"></span>
<button id="btn-expand" class="btn btn-link">展开筛选项</button>
</div>
<!-- 金额汇总 -->
<div id="summary-bar" class="summary-bar hidden">
金额汇总: <strong id="total-amount">¥0.00</strong>
</div>
<!-- 数据表格 -->
<div class="table-wrapper">
<table class="data-table" id="data-table">
<thead>
<tr>
<th class="col-time">时间</th>
<th class="col-person">人物</th>
<th class="col-location">地点</th>
<th class="col-content">事项内容</th>
<th class="col-amount">金额</th>
<th class="col-category">分类</th>
</tr>
</thead>
<tbody id="table-body">
<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div id="pagination" class="pagination hidden">
<button id="btn-prev" class="btn btn-page">&lt; 上一页</button>
<span id="page-info"></span>
<button id="btn-next" class="btn btn-page">下一页 &gt;</button>
</div>
</main>
</div>
<script src="/js/app.js"></script>
<script>
(function() {
var currentPage = 1;
var pageSize = 20;
var totalPages = 0;
var filterCollapsed = false;
var selectedPersons = [];
var selectedLocations = [];
var selectedCategories = [];
function init() {
loadFilters();
document.getElementById('btn-query').addEventListener('click', function() {
doQuery(1);
});
document.getElementById('btn-reset').addEventListener('click', function() {
resetFilters();
});
document.getElementById('btn-expand').addEventListener('click', function() {
toggleFilterSection(false);
});
document.getElementById('btn-prev').addEventListener('click', function() {
if (currentPage > 1) doQuery(currentPage - 1);
});
document.getElementById('btn-next').addEventListener('click', function() {
if (currentPage < totalPages) doQuery(currentPage + 1);
});
}
function loadFilters() {
fetch('/api/records/filters')
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
var data = resp.data;
renderFilterTags('filter-persons', data.persons || [], 'person');
renderFilterTags('filter-locations', data.locations || [], 'location');
renderFilterTags('filter-categories', data.categories || [], 'category');
});
}
function renderFilterTags(containerId, items, type) {
var container = document.getElementById(containerId);
container.innerHTML = '';
items.forEach(function(item) {
var tag = document.createElement('span');
tag.className = 'tag-btn';
tag.textContent = item;
tag.dataset.type = type;
tag.dataset.value = item;
tag.addEventListener('click', function() {
this.classList.toggle('active');
});
container.appendChild(tag);
});
}
function getSelectedTags(type) {
var tags = document.querySelectorAll('.tag-btn[data-type="' + type + '"].active');
return Array.from(tags).map(function(t) { return t.dataset.value; });
}
function getQueryParams() {
selectedPersons = getSelectedTags('person');
selectedLocations = getSelectedTags('location');
selectedCategories = getSelectedTags('category');
var startDate = document.getElementById('filter-start-date').value;
var endDate = document.getElementById('filter-end-date').value;
return {
persons: selectedPersons,
locations: selectedLocations,
categories: selectedCategories,
startDate: startDate,
endDate: endDate
};
}
function buildFilterSummary(params) {
var parts = [];
if (params.persons.length) parts.push('人物(' + params.persons.join(',') + ')');
if (params.locations.length) parts.push('地点(' + params.locations.join(',') + ')');
if (params.categories.length) parts.push('分类(' + params.categories.join(',') + ')');
if (params.startDate || params.endDate) {
parts.push('时间(' + (params.startDate || '不限') + '~' + (params.endDate || '不限') + ')');
}
return parts.join(' | ') || '无条件';
}
function toggleFilterSection(collapsed) {
filterCollapsed = collapsed;
document.getElementById('filter-section').classList.toggle('hidden', collapsed);
document.getElementById('filter-summary').classList.toggle('hidden', !collapsed);
}
function doQuery(page) {
var params = getQueryParams();
var queryStr = '?page=' + page + '&size=' + pageSize;
if (params.persons.length) {
params.persons.forEach(function(p) { queryStr += '&persons=' + encodeURIComponent(p); });
}
if (params.locations.length) {
params.locations.forEach(function(l) { queryStr += '&locations=' + encodeURIComponent(l); });
}
if (params.categories.length) {
params.categories.forEach(function(c) { queryStr += '&categories=' + encodeURIComponent(c); });
}
if (params.startDate) queryStr += '&startDate=' + params.startDate;
if (params.endDate) queryStr += '&endDate=' + params.endDate;
fetch('/api/records' + queryStr)
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
renderTable(resp.data);
toggleFilterSection(true);
var summary = document.getElementById('filter-summary-text');
summary.textContent = '已选: ' + buildFilterSummary(params);
});
}
function renderTable(data) {
if (!data) return;
var records = data.records || [];
var totalAmount = data.totalAmount || 0;
var total = data.total || 0;
currentPage = Number(data.page) || 1;
totalPages = Math.ceil(total / pageSize);
// 更新金额汇总
var summaryBar = document.getElementById('summary-bar');
summaryBar.classList.remove('hidden');
document.getElementById('total-amount').textContent = '¥' + Number(totalAmount).toFixed(2);
// 更新表格
var tbody = document.getElementById('table-body');
if (records.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="empty-cell">暂无数据</td></tr>';
} else {
var html = '';
records.forEach(function(r) {
html += '<tr>' +
'<td>' + formatTime(r.time) + '</td>' +
'<td>' + (r.person || '我') + '</td>' +
'<td>' + (r.location || '-') + '</td>' +
'<td>' + (r.content || '') + '</td>' +
'<td class="col-amount-cell">¥' + (r.amount ? Number(r.amount).toFixed(2) : '0.00') + '</td>' +
'<td><span class="badge badge ' + badgeClass(r.category) + '">' +
(r.category || '-') + '</span></td>' +
'</tr>';
});
tbody.innerHTML = html;
}
// 更新分页
var pagination = document.getElementById('pagination');
if (totalPages > 1) {
pagination.classList.remove('hidden');
document.getElementById('page-info').textContent =
'第 ' + currentPage + ' / ' + totalPages + ' 页(共 ' + total + ' 条)';
document.getElementById('btn-prev').disabled = currentPage <= 1;
document.getElementById('btn-next').disabled = currentPage >= totalPages;
} else {
pagination.classList.add('hidden');
}
}
function resetFilters() {
document.querySelectorAll('.tag-btn').forEach(function(t) { t.classList.remove('active'); });
document.getElementById('filter-start-date').value = '';
document.getElementById('filter-end-date').value = '';
document.getElementById('table-body').innerHTML =
'<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>';
document.getElementById('summary-bar').classList.add('hidden');
document.getElementById('pagination').classList.add('hidden');
toggleFilterSection(false);
}
init();
})();
</script>
</body>
</html>

View File

@ -0,0 +1,329 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>TraceXD - 首页</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header class="app-header">
<span class="header-title">TraceXD</span>
<nav class="header-nav">
<a href="/browse" class="header-link">浏览记录</a>
<a href="#" onclick="logout(); return false;" class="header-logout">退出</a>
</nav>
</header>
<main class="main-content">
<!-- 结果展示区 -->
<div id="result-area" class="result-area"></div>
<!-- Toast 通知 -->
<div id="toast" class="toast hidden"></div>
<!-- 最近事项 -->
<div class="recent-section">
<h3 class="section-title">最近事项</h3>
<div id="recent-list" class="recent-list">
<p class="empty-hint">暂无事项记录,点击右下角麦克风开始录入</p>
</div>
</div>
</main>
<!-- 麦克风按钮 -->
<button id="mic-btn" class="mic-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mic-icon">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
<line x1="12" y1="19" x2="12" y2="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
</button>
</div>
<script src="/js/app.js"></script>
<script>
(function() {
const micBtn = document.getElementById('mic-btn');
const resultArea = document.getElementById('result-area');
const toast = document.getElementById('toast');
const recentList = document.getElementById('recent-list');
let isRecording = false;
let stream = null;
// Web Audio API WAV recording variables
let audioCtx = null;
let mediaStreamSrc = null;
let procNode = null;
let pcmChunks = [];
let recordedSampleRate = 48000;
// 时间格式化
function formatTime(isoString) {
if (!isoString) return '';
const d = new Date(isoString);
const pad = n => String(n).padStart(2, '0');
return (d.getMonth() + 1) + '-' + d.getDate() + ' ' +
pad(d.getHours()) + ':' + pad(d.getMinutes());
}
// 显示 Toast
function showToast(message, type) {
toast.textContent = message;
toast.className = 'toast toast-' + (type || 'success') + ' show';
setTimeout(function() {
toast.className = 'toast hidden';
}, 3000);
}
// 展示查询结果
function showQueryResult(data) {
var html = '<div class="query-result-card">';
if (data.queryData && data.queryData.length > 0) {
html += '<table class="query-table"><thead><tr>' +
'<th>时间</th><th>人物</th><th>内容</th><th>金额</th>' +
'</tr></thead><tbody>';
data.queryData.forEach(function(row) {
html += '<tr class="' + cardClass(row.category) + '"><td>' + formatTime(row.time) + '</td>' +
'<td>' + (row.person || '') + '</td>' +
'<td>' + (row.content || '') + '</td>' +
'<td>' + (row.amount || '0') + '</td></tr>';
});
html += '</tbody></table>';
}
if (data.reply) {
html += '<div class="ai-summary">' + data.reply + '</div>';
}
html += '</div>';
resultArea.innerHTML = html;
}
// 展示AI直接回复其他意图
function showAiReply(text) {
var html = '<div class="query-result-card"><div class="ai-summary">' +
text + '</div></div>';
resultArea.innerHTML = html;
}
// 开始录音
function startRecording() {
pcmChunks = [];
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function(mediaStream) {
stream = mediaStream;
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
recordedSampleRate = audioCtx.sampleRate;
mediaStreamSrc = audioCtx.createMediaStreamSource(mediaStream);
procNode = audioCtx.createScriptProcessor(4096, 1, 1);
procNode.onaudioprocess = function(event) {
var channelData = event.inputBuffer.getChannelData(0);
var buf = new Int16Array(channelData.length);
for (var i = 0; i < channelData.length; i++) {
var s = Math.max(-1, Math.min(1, channelData[i]));
buf[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
}
pcmChunks.push(buf);
};
mediaStreamSrc.connect(procNode);
procNode.connect(audioCtx.destination);
isRecording = true;
micBtn.classList.add('recording');
})
.catch(function(err) {
showToast('无法访问麦克风 ' + err.message, 'error');
});
}
function stopRecording() {
if (!isRecording) return;
isRecording = false;
micBtn.classList.remove('recording');
if (procNode) {
procNode.disconnect();
procNode = null;
}
if (mediaStreamSrc) {
mediaStreamSrc.disconnect();
mediaStreamSrc = null;
}
if (audioCtx) {
audioCtx.close();
audioCtx = null;
}
if (stream) {
stream.getTracks().forEach(function(t) { t.stop(); });
stream = null;
}
if (pcmChunks.length === 0) return;
var totalLen = pcmChunks.reduce(function(sum, chunk) { return sum + chunk.length; }, 0);
var allSamples = new Int16Array(totalLen);
var offset = 0;
pcmChunks.forEach(function(chunk) {
allSamples.set(chunk, offset);
offset += chunk.length;
});
var wavBlob = encodeWav(allSamples, recordedSampleRate);
sendAudio(wavBlob);
}
function encodeWav(samples, sampleRate) {
var numChannels = 1;
var bitsPerSample = 16;
var bytesPerSample = bitsPerSample / 8;
var byteRate = sampleRate * numChannels * bytesPerSample;
var blockAlign = numChannels * bytesPerSample;
var dataSize = samples.length * bytesPerSample;
var buf = new ArrayBuffer(44 + dataSize);
var dv = new DataView(buf);
function writeStr(offset, str) {
for (var i = 0; i < str.length; i++) {
dv.setUint8(offset + i, str.charCodeAt(i));
}
}
writeStr(0, 'RIFF');
dv.setUint32(4, 36 + dataSize, true);
writeStr(8, 'WAVE');
writeStr(12, 'fmt ');
dv.setUint32(16, 16, true);
dv.setUint16(20, 1, true);
dv.setUint16(22, numChannels, true);
dv.setUint32(24, sampleRate, true);
dv.setUint32(28, byteRate, true);
dv.setUint16(32, blockAlign, true);
dv.setUint16(34, bitsPerSample, true);
writeStr(36, 'data');
dv.setUint32(40, dataSize, true);
var ptr = 44;
for (var i = 0; i < samples.length; i++) {
dv.setInt16(ptr, samples[i], true);
ptr += 2;
}
return new Blob([buf], { type: 'audio/wav' });
}
function cardClass(category) {
if (!category) return 'card-default';
var fixed = {'\u9910\u996e':'card-c1','\u996e\u98df':'card-c1','\u4ea4\u901a':'card-c2','\u51fa\u884c':'card-c2','\u8d2d\u7269':'card-c3','\u5a31\u4e50':'card-c4','\u533b\u7597':'card-c5','\u5065\u5eb7':'card-c5','\u6559\u80b2':'card-c6','\u5b66\u4e60':'card-c6'};
if (fixed[category]) return fixed[category];
var hash = 0;
for (var i = 0; i < category.length; i++) {
hash = ((hash << 5) - hash) + category.charCodeAt(i);
hash |= 0;
}
return 'card-c' + ((Math.abs(hash) % 12) + 1);
}
function sendAudio(blob) {
showToast('处理中...', 'info');
var formData = new FormData();
formData.append('audio', blob, 'recording.wav');
fetch('/api/voice/process', { method: 'POST', body: formData })
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) {
showToast(resp.message, 'error');
return;
}
var taskId = resp.data.taskId;
var es = new EventSource('/api/voice/stream/' + taskId);
es.onmessage = function(event) {
var result = JSON.parse(event.data);
es.close();
handleVoiceResult(result);
};
es.onerror = function() {
es.close();
showToast('处理超时,请重试', 'error');
};
})
.catch(function(err) {
showToast('网络错误: ' + err.message, 'error');
});
}
function handleVoiceResult(result) {
if (result.code !== 200) {
showToast(result.message, 'error');
return;
}
var data = result.data;
if (data.intent === 'RECORD') {
if (data.success && data.recordTip) {
showToast(data.recordTip, 'success');
loadRecentRecords();
} else {
showToast(data.reply || '录入失败', 'error');
}
} else if (data.intent === 'QUERY') {
showQueryResult(data);
showToast('查询完成', 'info');
} else {
showAiReply(data.reply);
}
}
// 加载最近事项
function loadRecentRecords() {
fetch('/api/records?page=1&size=5')
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200 || !resp.data) return;
var records = resp.data.records || [];
if (records.length === 0) {
recentList.innerHTML = '<p class="empty-hint">暂无事项记录</p>';
return;
}
var html = '';
records.forEach(function(r) {
html += '<div class="record-item ' + cardClass(r.category) + '">' +
'<span class="rec-time">' + formatTime(r.time) + '</span>' +
'<span class="rec-person">' + (r.person || '我') + '</span>' +
'<span class="rec-content">' + (r.content || '') + '</span>' +
'<span class="rec-amount">¥' + (r.amount || '0') + '</span>' +
'</div>';
});
recentList.innerHTML = html;
})
.catch(function() {});
}
// 事件绑定
micBtn.addEventListener('mousedown', startRecording);
micBtn.addEventListener('mouseup', stopRecording);
micBtn.addEventListener('mouseleave', stopRecording);
micBtn.addEventListener('touchstart', function(e) {
e.preventDefault();
startRecording();
});
micBtn.addEventListener('touchend', function(e) {
e.preventDefault();
stopRecording();
});
// 初始化加载最近事项
loadRecentRecords();
})();
</script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 - TraceXD</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body class="login-body">
<div class="login-container">
<h1 class="login-title">TraceXD</h1>
<p class="login-subtitle">日常事项记录系统</p>
<form class="login-form" method="post" th:action="@{/login}">
<div th:if="${param.error}" class="login-error">
用户名或密码错误
</div>
<div th:if="${param.logout}" class="login-info">
您已退出登录
</div>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-remember">
<label>
<input type="checkbox" name="remember-me" checked> 记住我30天免登录
</label>
</div>
<button type="submit" class="login-btn">登 录</button>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
com\tracexd\model\dto\VoiceProcessResult.class
com\tracexd\common\Result.class
com\tracexd\mapper\SysUserMapper.class
com\tracexd\mapper\DictTagMapper.class
com\tracexd\service\ai\AiService$IntentResult.class
com\tracexd\config\MybatisPlusConfig.class
com\tracexd\enums\IntentType.class
com\tracexd\common\exception\GlobalExceptionHandler.class
com\tracexd\model\entity\SysUser.class
com\tracexd\service\ai\AiService.class
com\tracexd\controller\RecordController.class
com\tracexd\common\exception\BusinessException.class
com\tracexd\common\VoiceTaskManager.class
com\tracexd\TracexdApplication.class
com\tracexd\service\record\RecordService.class
com\tracexd\model\dto\RecordQueryDTO.class
com\tracexd\config\AsyncConfig.class
com\tracexd\controller\PageController.class
com\tracexd\config\SecurityConfig.class
com\tracexd\service\record\DictTagService.class
com\tracexd\service\voice\VoiceProcessService.class
com\tracexd\service\voice\VoiceProcessService$1.class
com\tracexd\service\asr\AsrService.class
com\tracexd\model\entity\Record.class
com\tracexd\service\sql\SqlValidator.class
com\tracexd\config\RestClientConfig.class
com\tracexd\controller\VoiceController.class
com\tracexd\model\entity\DictTag.class
com\tracexd\mapper\RecordMapper.class
com\tracexd\enums\DictTypeEnum.class

View File

@ -0,0 +1,28 @@
C:\Work\tracexd\src\main\java\com\tracexd\common\exception\BusinessException.java
C:\Work\tracexd\src\main\java\com\tracexd\common\exception\GlobalExceptionHandler.java
C:\Work\tracexd\src\main\java\com\tracexd\common\Result.java
C:\Work\tracexd\src\main\java\com\tracexd\common\VoiceTaskManager.java
C:\Work\tracexd\src\main\java\com\tracexd\config\AsyncConfig.java
C:\Work\tracexd\src\main\java\com\tracexd\config\MybatisPlusConfig.java
C:\Work\tracexd\src\main\java\com\tracexd\config\RestClientConfig.java
C:\Work\tracexd\src\main\java\com\tracexd\config\SecurityConfig.java
C:\Work\tracexd\src\main\java\com\tracexd\controller\PageController.java
C:\Work\tracexd\src\main\java\com\tracexd\controller\RecordController.java
C:\Work\tracexd\src\main\java\com\tracexd\controller\VoiceController.java
C:\Work\tracexd\src\main\java\com\tracexd\enums\DictTypeEnum.java
C:\Work\tracexd\src\main\java\com\tracexd\enums\IntentType.java
C:\Work\tracexd\src\main\java\com\tracexd\mapper\DictTagMapper.java
C:\Work\tracexd\src\main\java\com\tracexd\mapper\RecordMapper.java
C:\Work\tracexd\src\main\java\com\tracexd\mapper\SysUserMapper.java
C:\Work\tracexd\src\main\java\com\tracexd\model\dto\RecordQueryDTO.java
C:\Work\tracexd\src\main\java\com\tracexd\model\dto\VoiceProcessResult.java
C:\Work\tracexd\src\main\java\com\tracexd\model\entity\DictTag.java
C:\Work\tracexd\src\main\java\com\tracexd\model\entity\Record.java
C:\Work\tracexd\src\main\java\com\tracexd\model\entity\SysUser.java
C:\Work\tracexd\src\main\java\com\tracexd\service\ai\AiService.java
C:\Work\tracexd\src\main\java\com\tracexd\service\asr\AsrService.java
C:\Work\tracexd\src\main\java\com\tracexd\service\record\DictTagService.java
C:\Work\tracexd\src\main\java\com\tracexd\service\record\RecordService.java
C:\Work\tracexd\src\main\java\com\tracexd\service\sql\SqlValidator.java
C:\Work\tracexd\src\main\java\com\tracexd\service\voice\VoiceProcessService.java
C:\Work\tracexd\src\main\java\com\tracexd\TracexdApplication.java

View File

@ -0,0 +1 @@
com\tracexd\TracexdApplicationTests.class

View File

@ -0,0 +1 @@
C:\Work\tracexd\src\test\java\com\tracexd\TracexdApplicationTests.java