feat: 完成Nacos配置优化和WebSocket集成

主要更新:
1. 统一所有微服务端口配置(19000-19008)
2. 为所有服务创建本地/测试/生产三套环境配置
3. 配置Nacos认证密码(本地:Peanut2817*#, 测试/生产:EmotionMuseum2025)
4. 优化网关路由配置,支持负载均衡和WebSocket
5. 新增emotion-websocket模块,支持实时聊天
6. 前端集成WebSocket,替代HTTP轮询
7. 添加配置验证和管理工具脚本

技术特性:
- 完整的环境隔离和服务发现
- WebSocket实时通信支持
- 负载均衡路由配置
- 跨域和安全配置
- 自动重连和心跳检测
This commit is contained in:
2025-07-17 18:10:45 +08:00
parent 9a3a8267b5
commit c77352877d
391 changed files with 46585 additions and 4294 deletions
@@ -0,0 +1,305 @@
2025-07-16T08:59:01.762+08:00 WARN 36358 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2025-07-16T08:59:01.762+08:00 WARN 36358 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Start destroying Publisher
2025-07-16T08:59:01.864+08:00 WARN 36358 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Destruction of the end
2025-07-16T08:59:01.871+08:00 WARN 36358 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Destruction of the end
2025-07-16T08:59:02.053+08:00 INFO 36358 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-07-16T08:59:02.060+08:00 INFO 36358 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2025-07-16T08:59:19.149+08:00 WARN 15828 --- [main] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T08:59:19.178+08:00 INFO 15828 --- [main] c.a.n.c.c.impl.LocalConfigInfoProcessor : LOCAL_SNAPSHOT_PATH:/Users/huazhongmin/nacos/config
2025-07-16T08:59:19.182+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [RpcClientFactory] create a new rpc client of f264096d-5a79-4318-9f7b-d1a3577abfef_config-0
2025-07-16T08:59:19.203+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x000000012f2dc260
2025-07-16T08:59:19.204+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x000000012f2dc670
2025-07-16T08:59:19.204+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Registry connection listener to current client:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$1
2025-07-16T08:59:19.204+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] RpcClient init, ServerListFactory = com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$2
2025-07-16T08:59:19.218+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Try to connect to server on start up, server: {serverIp = '127.0.0.1', server main port = 8848}
2025-07-16T08:59:19.263+08:00 INFO 15828 --- [main] c.a.n.c.remote.client.grpc.GrpcClient : grpc client connection server:127.0.0.1 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"OPENSSL","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
2025-07-16T08:59:19.932+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Success to connect to server [127.0.0.1:8848] on start up, connectionId = 1752627559740_127.0.0.1_61370
2025-07-16T08:59:19.933+08:00 INFO 15828 --- [com.alibaba.nacos.client.remote.worker] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Notify connected event to listeners.
2025-07-16T08:59:19.933+08:00 INFO 15828 --- [com.alibaba.nacos.client.remote.worker] c.a.n.client.config.impl.ClientWorker : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Connected,notify listen context...
2025-07-16T08:59:19.933+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
2025-07-16T08:59:19.934+08:00 INFO 15828 --- [main] com.alibaba.nacos.common.remote.client : [f264096d-5a79-4318-9f7b-d1a3577abfef_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda/0x000000012f43f118
2025-07-16T08:59:19.978+08:00 INFO 15828 --- [main] c.a.nacos.client.config.impl.Limiter : limitTime:5.0
2025-07-16T08:59:20.036+08:00 WARN 15828 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user] & group[DEFAULT_GROUP]
2025-07-16T08:59:20.042+08:00 WARN 15828 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user.properties] & group[DEFAULT_GROUP]
2025-07-16T08:59:20.051+08:00 WARN 15828 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user-local.properties] & group[DEFAULT_GROUP]
2025-07-16T08:59:20.052+08:00 INFO 15828 --- [main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-emotion-user-local.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user,DEFAULT_GROUP'}]
2025-07-16T08:59:20.079+08:00 WARN 15828 --- [main] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T08:59:20.080+08:00 INFO 15828 --- [main] com.emotionmuseum.user.UserApplication : The following 1 profile is active: "local"
2025-07-16T08:59:21.224+08:00 INFO 15828 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2025-07-16T08:59:21.229+08:00 INFO 15828 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-07-16T08:59:21.266+08:00 INFO 15828 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17 ms. Found 0 Redis repository interfaces.
2025-07-16T08:59:21.689+08:00 INFO 15828 --- [main] o.s.cloud.context.scope.GenericScope : BeanFactory id=2c694ed0-114c-30e6-aed7-dcee6bca36f0
2025-07-16T08:59:22.421+08:00 INFO 15828 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 19001 (http)
2025-07-16T08:59:22.451+08:00 INFO 15828 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-07-16T08:59:22.452+08:00 INFO 15828 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2025-07-16T08:59:22.557+08:00 INFO 15828 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-07-16T08:59:22.558+08:00 INFO 15828 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2449 ms
2025-07-16T08:59:23.525+08:00 INFO 15828 --- [main] c.e.common.config.SnowflakeConfig : 使用MAC地址生成的机器ID: 669
2025-07-16T08:59:23.527+08:00 INFO 15828 --- [main] c.e.common.config.SnowflakeConfig : 雪花算法配置完成,使用机器ID: 669
2025-07-16T08:59:23.527+08:00 INFO 15828 --- [main] c.e.common.util.SnowflakeIdGenerator : 雪花算法ID生成器初始化完成,机器ID: 669
2025-07-16T08:59:24.050+08:00 DEBUG 15828 --- [main] c.e.u.security.JwtAuthenticationFilter : Filter 'jwtAuthenticationFilter' configured for use
2025-07-16T08:59:24.054+08:00 DEBUG 15828 --- [main] o.s.w.f.ServerHttpObservationFilter : Filter 'serverHttpObservationFilter' configured for use
2025-07-16T08:59:24.740+08:00 DEBUG 15828 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : 19 mappings in 'requestMappingHandlerMapping'
2025-07-16T08:59:24.773+08:00 DEBUG 15828 --- [main] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2025-07-16T08:59:25.010+08:00 INFO 15828 --- [main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
2025-07-16T08:59:25.104+08:00 INFO 15828 --- [main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@6993c8df, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@57545c3f, org.springframework.security.web.context.SecurityContextHolderFilter@64920dc2, org.springframework.security.web.header.HeaderWriterFilter@794366a5, org.springframework.web.filter.CorsFilter@326e0b8e, org.springframework.security.web.authentication.logout.LogoutFilter@30839e44, com.emotionmuseum.user.security.JwtAuthenticationFilter@434514d8, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@493ac8d3, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@13dbed9e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@36baa049, org.springframework.security.web.session.SessionManagementFilter@1ee47d9e, org.springframework.security.web.access.ExceptionTranslationFilter@3f36e8d1, org.springframework.security.web.access.intercept.AuthorizationFilter@7978e022]
2025-07-16T08:59:25.230+08:00 DEBUG 15828 --- [main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2025-07-16T08:59:25.275+08:00 DEBUG 15828 --- [main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2025-07-16T08:59:25.597+08:00 INFO 15828 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 19001 (http) with context path ''
2025-07-16T08:59:25.626+08:00 INFO 15828 --- [main] com.emotionmuseum.user.UserApplication : Started UserApplication in 7.738 seconds (process running for 8.451)
2025-07-16T08:59:57.321+08:00 INFO 15828 --- [http-nio-19001-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-07-16T08:59:57.322+08:00 INFO 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-07-16T08:59:57.322+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected StandardServletMultipartResolver
2025-07-16T08:59:57.322+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected AcceptHeaderLocaleResolver
2025-07-16T08:59:57.322+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected FixedThemeResolver
2025-07-16T08:59:57.323+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@35e7e45f
2025-07-16T08:59:57.323+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.support.SessionFlashMapManager@5dfd939f
2025-07-16T08:59:57.323+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2025-07-16T08:59:57.324+08:00 INFO 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2025-07-16T08:59:57.373+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : GET "/actuator/health", parameters={}
2025-07-16T08:59:57.404+08:00 INFO 15828 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-07-16T08:59:57.629+08:00 INFO 15828 --- [http-nio-19001-exec-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@4cf3c80b
2025-07-16T08:59:57.633+08:00 INFO 15828 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-07-16T08:59:58.042+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/vnd.spring-boot.actuator.v3+json', given [*/*] and supported [application/vnd.spring-boot.actuator.v3+json, application/vnd.spring-boot.actuator.v2+json, application/json]
2025-07-16T08:59:58.052+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.springframework.boot.actuate.health.SystemHealth@1ec7b554]
2025-07-16T08:59:58.069+08:00 DEBUG 15828 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2025-07-16T09:01:08.310+08:00 DEBUG 15828 --- [http-nio-19001-exec-3] o.s.web.servlet.DispatcherServlet : GET "/actuator/health", parameters={}
2025-07-16T09:01:08.316+08:00 DEBUG 15828 --- [http-nio-19001-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/vnd.spring-boot.actuator.v3+json', given [*/*] and supported [application/vnd.spring-boot.actuator.v3+json, application/vnd.spring-boot.actuator.v2+json, application/json]
2025-07-16T09:01:08.316+08:00 DEBUG 15828 --- [http-nio-19001-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.springframework.boot.actuate.health.SystemHealth@6fbe5cda]
2025-07-16T09:01:08.317+08:00 DEBUG 15828 --- [http-nio-19001-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2025-07-16T09:03:03.679+08:00 WARN 15828 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Start destroying Publisher
2025-07-16T09:03:03.679+08:00 WARN 15828 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2025-07-16T09:03:03.680+08:00 WARN 15828 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Destruction of the end
2025-07-16T09:03:03.680+08:00 WARN 15828 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Destruction of the end
2025-07-16T09:03:03.697+08:00 INFO 15828 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-07-16T09:03:03.705+08:00 INFO 15828 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2025-07-16T09:03:19.624+08:00 WARN 19784 --- [main] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:03:19.652+08:00 INFO 19784 --- [main] c.a.n.c.c.impl.LocalConfigInfoProcessor : LOCAL_SNAPSHOT_PATH:/Users/huazhongmin/nacos/config
2025-07-16T09:03:19.656+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [RpcClientFactory] create a new rpc client of 745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0
2025-07-16T09:03:19.675+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x000000013d2dc530
2025-07-16T09:03:19.676+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x000000013d2dc940
2025-07-16T09:03:19.676+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Registry connection listener to current client:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$1
2025-07-16T09:03:19.677+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] RpcClient init, ServerListFactory = com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$2
2025-07-16T09:03:19.689+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Try to connect to server on start up, server: {serverIp = '127.0.0.1', server main port = 8848}
2025-07-16T09:03:19.748+08:00 INFO 19784 --- [main] c.a.n.c.remote.client.grpc.GrpcClient : grpc client connection server:127.0.0.1 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"OPENSSL","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
2025-07-16T09:03:20.358+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Success to connect to server [127.0.0.1:8848] on start up, connectionId = 1752627800162_127.0.0.1_62095
2025-07-16T09:03:20.358+08:00 INFO 19784 --- [com.alibaba.nacos.client.remote.worker] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Notify connected event to listeners.
2025-07-16T09:03:20.359+08:00 INFO 19784 --- [com.alibaba.nacos.client.remote.worker] c.a.n.client.config.impl.ClientWorker : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Connected,notify listen context...
2025-07-16T09:03:20.359+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
2025-07-16T09:03:20.359+08:00 INFO 19784 --- [main] com.alibaba.nacos.common.remote.client : [745c3cf4-7cb2-4ac6-a11a-86fd37de7293_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda/0x000000013d43f118
2025-07-16T09:03:20.394+08:00 INFO 19784 --- [main] c.a.nacos.client.config.impl.Limiter : limitTime:5.0
2025-07-16T09:03:20.411+08:00 WARN 19784 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user] & group[DEFAULT_GROUP]
2025-07-16T09:03:20.417+08:00 WARN 19784 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user.properties] & group[DEFAULT_GROUP]
2025-07-16T09:03:20.422+08:00 WARN 19784 --- [main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user-local.properties] & group[DEFAULT_GROUP]
2025-07-16T09:03:20.423+08:00 INFO 19784 --- [main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-emotion-user-local.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user,DEFAULT_GROUP'}]
2025-07-16T09:03:20.445+08:00 WARN 19784 --- [main] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:03:20.446+08:00 INFO 19784 --- [main] com.emotionmuseum.user.UserApplication : The following 1 profile is active: "local"
2025-07-16T09:03:21.462+08:00 INFO 19784 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2025-07-16T09:03:21.466+08:00 INFO 19784 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-07-16T09:03:21.498+08:00 INFO 19784 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 12 ms. Found 0 Redis repository interfaces.
2025-07-16T09:03:21.895+08:00 INFO 19784 --- [main] o.s.cloud.context.scope.GenericScope : BeanFactory id=2c694ed0-114c-30e6-aed7-dcee6bca36f0
2025-07-16T09:03:22.569+08:00 INFO 19784 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 19001 (http)
2025-07-16T09:03:22.584+08:00 INFO 19784 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-07-16T09:03:22.585+08:00 INFO 19784 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2025-07-16T09:03:22.681+08:00 INFO 19784 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-07-16T09:03:22.682+08:00 INFO 19784 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2214 ms
2025-07-16T09:03:23.440+08:00 INFO 19784 --- [main] c.e.common.config.SnowflakeConfig : 使用MAC地址生成的机器ID: 669
2025-07-16T09:03:23.440+08:00 INFO 19784 --- [main] c.e.common.config.SnowflakeConfig : 雪花算法配置完成,使用机器ID: 669
2025-07-16T09:03:23.440+08:00 INFO 19784 --- [main] c.e.common.util.SnowflakeIdGenerator : 雪花算法ID生成器初始化完成,机器ID: 669
2025-07-16T09:03:24.033+08:00 DEBUG 19784 --- [main] c.e.u.security.JwtAuthenticationFilter : Filter 'jwtAuthenticationFilter' configured for use
2025-07-16T09:03:24.037+08:00 DEBUG 19784 --- [main] o.s.w.f.ServerHttpObservationFilter : Filter 'serverHttpObservationFilter' configured for use
2025-07-16T09:03:24.837+08:00 DEBUG 19784 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : 19 mappings in 'requestMappingHandlerMapping'
2025-07-16T09:03:24.878+08:00 DEBUG 19784 --- [main] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2025-07-16T09:03:25.069+08:00 INFO 19784 --- [main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
2025-07-16T09:03:25.128+08:00 INFO 19784 --- [main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@6993c8df, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@57545c3f, org.springframework.security.web.context.SecurityContextHolderFilter@64920dc2, org.springframework.security.web.header.HeaderWriterFilter@794366a5, org.springframework.web.filter.CorsFilter@326e0b8e, org.springframework.security.web.authentication.logout.LogoutFilter@30839e44, com.emotionmuseum.user.security.JwtAuthenticationFilter@434514d8, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@493ac8d3, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@13dbed9e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@36baa049, org.springframework.security.web.session.SessionManagementFilter@1ee47d9e, org.springframework.security.web.access.ExceptionTranslationFilter@3f36e8d1, org.springframework.security.web.access.intercept.AuthorizationFilter@7978e022]
2025-07-16T09:03:25.229+08:00 DEBUG 19784 --- [main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2025-07-16T09:03:25.268+08:00 DEBUG 19784 --- [main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2025-07-16T09:03:25.658+08:00 INFO 19784 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 19001 (http) with context path ''
2025-07-16T09:03:25.689+08:00 INFO 19784 --- [main] com.emotionmuseum.user.UserApplication : Started UserApplication in 7.27 seconds (process running for 7.838)
2025-07-16T09:03:57.442+08:00 INFO 19784 --- [http-nio-19001-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-07-16T09:03:57.443+08:00 INFO 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-07-16T09:03:57.443+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected StandardServletMultipartResolver
2025-07-16T09:03:57.443+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected AcceptHeaderLocaleResolver
2025-07-16T09:03:57.443+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected FixedThemeResolver
2025-07-16T09:03:57.444+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@5e240cb6
2025-07-16T09:03:57.444+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.support.SessionFlashMapManager@2e21a5d9
2025-07-16T09:03:57.445+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2025-07-16T09:03:57.445+08:00 INFO 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2025-07-16T09:03:57.509+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : GET "/actuator/health", parameters={}
2025-07-16T09:03:57.546+08:00 INFO 19784 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-07-16T09:03:57.793+08:00 INFO 19784 --- [http-nio-19001-exec-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1703fbc9
2025-07-16T09:03:57.797+08:00 INFO 19784 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-07-16T09:03:58.405+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/vnd.spring-boot.actuator.v3+json', given [*/*] and supported [application/vnd.spring-boot.actuator.v3+json, application/vnd.spring-boot.actuator.v2+json, application/json]
2025-07-16T09:03:58.416+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.springframework.boot.actuate.health.SystemHealth@178369bf]
2025-07-16T09:03:58.433+08:00 DEBUG 19784 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2025-07-16T09:04:12.530+08:00 DEBUG 19784 --- [http-nio-19001-exec-3] o.s.web.servlet.DispatcherServlet : GET "/actuator/health", parameters={}
2025-07-16T09:04:12.544+08:00 DEBUG 19784 --- [http-nio-19001-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/vnd.spring-boot.actuator.v3+json', given [*/*] and supported [application/vnd.spring-boot.actuator.v3+json, application/vnd.spring-boot.actuator.v2+json, application/json]
2025-07-16T09:04:12.545+08:00 DEBUG 19784 --- [http-nio-19001-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.springframework.boot.actuate.health.SystemHealth@159e4af4]
2025-07-16T09:04:12.545+08:00 DEBUG 19784 --- [http-nio-19001-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2025-07-16T09:04:22.221+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.223+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.224+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.224+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.225+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.225+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.226+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.226+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.227+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.227+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.228+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.228+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.229+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2025-07-16T09:04:22.237+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.237+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.238+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.238+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.238+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.239+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.239+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.239+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.240+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.240+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.240+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.241+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.241+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.242+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}
2025-07-16T09:04:22.242+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-07-16T09:04:22.245+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json]
2025-07-16T09:04:22.245+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Wed Jul 16 09:04:22 CST 2025, status=403, error=Forbidden, path=/user/actuator/health}]
2025-07-16T09:04:22.249+08:00 DEBUG 19784 --- [http-nio-19001-exec-5] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 403
2025-07-16T09:45:38.808+08:00 WARN 56575 --- [restartedMain] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:45:38.828+08:00 INFO 56575 --- [restartedMain] c.a.n.c.c.impl.LocalConfigInfoProcessor : LOCAL_SNAPSHOT_PATH:/Users/huazhongmin/nacos/config
2025-07-16T09:45:38.832+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [RpcClientFactory] create a new rpc client of ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0
2025-07-16T09:45:38.850+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x00000001273239b8
2025-07-16T09:45:38.850+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x0000000127323de8
2025-07-16T09:45:38.851+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Registry connection listener to current client:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$1
2025-07-16T09:45:38.851+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] RpcClient init, ServerListFactory = com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$2
2025-07-16T09:45:38.857+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Try to connect to server on start up, server: {serverIp = '127.0.0.1', server main port = 8848}
2025-07-16T09:45:38.890+08:00 INFO 56575 --- [restartedMain] c.a.n.c.remote.client.grpc.GrpcClient : grpc client connection server:127.0.0.1 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"OPENSSL","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
2025-07-16T09:45:39.587+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Success to connect to server [127.0.0.1:8848] on start up, connectionId = 1752630339357_127.0.0.1_52952
2025-07-16T09:45:39.589+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
2025-07-16T09:45:39.589+08:00 INFO 56575 --- [com.alibaba.nacos.client.remote.worker] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Notify connected event to listeners.
2025-07-16T09:45:39.589+08:00 INFO 56575 --- [com.alibaba.nacos.client.remote.worker] c.a.n.client.config.impl.ClientWorker : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Connected,notify listen context...
2025-07-16T09:45:39.590+08:00 INFO 56575 --- [restartedMain] com.alibaba.nacos.common.remote.client : [ecde1dfa-90a6-49ad-97db-0a748b10d78c_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda/0x000000012748f730
2025-07-16T09:45:39.641+08:00 INFO 56575 --- [restartedMain] c.a.nacos.client.config.impl.Limiter : limitTime:5.0
2025-07-16T09:45:39.679+08:00 WARN 56575 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user] & group[DEFAULT_GROUP]
2025-07-16T09:45:39.684+08:00 WARN 56575 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user.properties] & group[DEFAULT_GROUP]
2025-07-16T09:45:39.691+08:00 WARN 56575 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user-local.properties] & group[DEFAULT_GROUP]
2025-07-16T09:45:39.691+08:00 INFO 56575 --- [restartedMain] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-emotion-user-local.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user,DEFAULT_GROUP'}]
2025-07-16T09:45:39.707+08:00 WARN 56575 --- [restartedMain] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:45:39.707+08:00 INFO 56575 --- [restartedMain] com.emotionmuseum.user.UserApplication : The following 1 profile is active: "local"
2025-07-16T09:45:40.656+08:00 INFO 56575 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2025-07-16T09:45:40.660+08:00 INFO 56575 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-07-16T09:45:40.691+08:00 INFO 56575 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 13 ms. Found 0 Redis repository interfaces.
2025-07-16T09:45:40.909+08:00 INFO 56575 --- [restartedMain] o.s.cloud.context.scope.GenericScope : BeanFactory id=46ac2086-60cb-34f0-84ae-496767f2402a
2025-07-16T09:45:41.566+08:00 INFO 56575 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 19001 (http)
2025-07-16T09:45:41.578+08:00 INFO 56575 --- [restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-07-16T09:45:41.578+08:00 INFO 56575 --- [restartedMain] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2025-07-16T09:45:41.632+08:00 INFO 56575 --- [restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-07-16T09:45:41.633+08:00 INFO 56575 --- [restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1902 ms
2025-07-16T09:45:42.183+08:00 INFO 56575 --- [restartedMain] c.e.common.config.SnowflakeConfig : 使用MAC地址生成的机器ID: 217
2025-07-16T09:45:42.183+08:00 INFO 56575 --- [restartedMain] c.e.common.config.SnowflakeConfig : 雪花算法配置完成,使用机器ID: 217
2025-07-16T09:45:42.183+08:00 INFO 56575 --- [restartedMain] c.e.common.util.SnowflakeIdGenerator : 雪花算法ID生成器初始化完成,机器ID: 217
2025-07-16T09:45:42.424+08:00 DEBUG 56575 --- [restartedMain] c.e.u.security.JwtAuthenticationFilter : Filter 'jwtAuthenticationFilter' configured for use
2025-07-16T09:45:42.426+08:00 DEBUG 56575 --- [restartedMain] o.s.w.f.ServerHttpObservationFilter : Filter 'serverHttpObservationFilter' configured for use
2025-07-16T09:45:43.011+08:00 DEBUG 56575 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : 19 mappings in 'requestMappingHandlerMapping'
2025-07-16T09:45:43.044+08:00 DEBUG 56575 --- [restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2025-07-16T09:45:43.167+08:00 INFO 56575 --- [restartedMain] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
2025-07-16T09:45:43.225+08:00 INFO 56575 --- [restartedMain] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@6197bfc3, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@19b14e61, org.springframework.security.web.context.SecurityContextHolderFilter@4c11c4d5, org.springframework.security.web.header.HeaderWriterFilter@467455d6, org.springframework.web.filter.CorsFilter@390f1c1, org.springframework.security.web.authentication.logout.LogoutFilter@335ca7f9, com.emotionmuseum.user.security.JwtAuthenticationFilter@32d98a3b, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@76defa49, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@52afd65a, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1624e51e, org.springframework.security.web.session.SessionManagementFilter@3aee12aa, org.springframework.security.web.access.ExceptionTranslationFilter@1c7422d0, org.springframework.security.web.access.intercept.AuthorizationFilter@6503bbb3]
2025-07-16T09:45:43.297+08:00 DEBUG 56575 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2025-07-16T09:45:43.336+08:00 DEBUG 56575 --- [restartedMain] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2025-07-16T09:45:43.536+08:00 INFO 56575 --- [restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2025-07-16T09:45:43.600+08:00 WARN 56575 --- [restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'
2025-07-16T09:45:43.618+08:00 INFO 56575 --- [restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2025-07-16T09:45:43.732+08:00 INFO 56575 --- [restartedMain] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-07-16T09:45:43.749+08:00 ERROR 56575 --- [restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 19001 was already in use.
Action:
Identify and stop the process that's listening on port 19001 or configure this application to listen on another port.
2025-07-16T09:46:36.096+08:00 WARN 56575 --- [Thread-10] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Start destroying Publisher
2025-07-16T09:46:36.096+08:00 WARN 56575 --- [Thread-4] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2025-07-16T09:46:36.096+08:00 WARN 56575 --- [Thread-10] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Destruction of the end
2025-07-16T09:46:48.938+08:00 WARN 19784 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Start destroying Publisher
2025-07-16T09:46:48.938+08:00 WARN 19784 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2025-07-16T09:46:48.964+08:00 WARN 19784 --- [Thread-7] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Destruction of the end
2025-07-16T09:46:48.973+08:00 WARN 19784 --- [Thread-1] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Destruction of the end
2025-07-16T09:46:49.141+08:00 INFO 19784 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-07-16T09:46:49.151+08:00 INFO 19784 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2025-07-16T09:48:19.128+08:00 WARN 59407 --- [restartedMain] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:48:19.155+08:00 INFO 59407 --- [restartedMain] c.a.n.c.c.impl.LocalConfigInfoProcessor : LOCAL_SNAPSHOT_PATH:/Users/huazhongmin/nacos/config
2025-07-16T09:48:19.158+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [RpcClientFactory] create a new rpc client of 55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0
2025-07-16T09:48:19.177+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x00000001283221c0
2025-07-16T09:48:19.177+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Register server push request handler:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$$Lambda/0x00000001283225f0
2025-07-16T09:48:19.177+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Registry connection listener to current client:com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$1
2025-07-16T09:48:19.177+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] RpcClient init, ServerListFactory = com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient$2
2025-07-16T09:48:19.183+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Try to connect to server on start up, server: {serverIp = '127.0.0.1', server main port = 8848}
2025-07-16T09:48:19.215+08:00 INFO 59407 --- [restartedMain] c.a.n.c.remote.client.grpc.GrpcClient : grpc client connection server:127.0.0.1 ip,serverPort:9848,grpcTslConfig:{"sslProvider":"OPENSSL","enableTls":false,"mutualAuthEnable":false,"trustAll":false}
2025-07-16T09:48:19.832+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Success to connect to server [127.0.0.1:8848] on start up, connectionId = 1752630499650_127.0.0.1_53418
2025-07-16T09:48:19.832+08:00 INFO 59407 --- [com.alibaba.nacos.client.remote.worker] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Notify connected event to listeners.
2025-07-16T09:48:19.832+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$ConnectResetRequestHandler
2025-07-16T09:48:19.832+08:00 INFO 59407 --- [com.alibaba.nacos.client.remote.worker] c.a.n.client.config.impl.ClientWorker : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Connected,notify listen context...
2025-07-16T09:48:19.833+08:00 INFO 59407 --- [restartedMain] com.alibaba.nacos.common.remote.client : [55011ce6-21c3-4e3d-aef7-8b1bc89c6930_config-0] Register server push request handler:com.alibaba.nacos.common.remote.client.RpcClient$$Lambda/0x000000012848b618
2025-07-16T09:48:19.871+08:00 INFO 59407 --- [restartedMain] c.a.nacos.client.config.impl.Limiter : limitTime:5.0
2025-07-16T09:48:19.889+08:00 WARN 59407 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user] & group[DEFAULT_GROUP]
2025-07-16T09:48:19.895+08:00 WARN 59407 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user.properties] & group[DEFAULT_GROUP]
2025-07-16T09:48:19.899+08:00 WARN 59407 --- [restartedMain] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[emotion-user-local.properties] & group[DEFAULT_GROUP]
2025-07-16T09:48:19.900+08:00 INFO 59407 --- [restartedMain] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-emotion-user-local.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-emotion-user,DEFAULT_GROUP'}]
2025-07-16T09:48:19.918+08:00 WARN 59407 --- [restartedMain] c.a.nacos.client.logging.NacosLogging : Load Logback Configuration of Nacos fail, message: Could not initialize Logback Nacos logging from classpath:nacos-logback.xml
2025-07-16T09:48:19.919+08:00 INFO 59407 --- [restartedMain] com.emotionmuseum.user.UserApplication : The following 1 profile is active: "local"
2025-07-16T09:48:20.767+08:00 INFO 59407 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2025-07-16T09:48:20.771+08:00 INFO 59407 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-07-16T09:48:20.797+08:00 INFO 59407 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 11 ms. Found 0 Redis repository interfaces.
2025-07-16T09:48:21.020+08:00 INFO 59407 --- [restartedMain] o.s.cloud.context.scope.GenericScope : BeanFactory id=46ac2086-60cb-34f0-84ae-496767f2402a
2025-07-16T09:48:21.786+08:00 INFO 59407 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 19001 (http)
2025-07-16T09:48:21.797+08:00 INFO 59407 --- [restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-07-16T09:48:21.798+08:00 INFO 59407 --- [restartedMain] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.5]
2025-07-16T09:48:21.851+08:00 INFO 59407 --- [restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-07-16T09:48:21.851+08:00 INFO 59407 --- [restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1904 ms
2025-07-16T09:48:22.859+08:00 INFO 59407 --- [restartedMain] c.e.common.config.SnowflakeConfig : 使用MAC地址生成的机器ID: 652
2025-07-16T09:48:22.860+08:00 INFO 59407 --- [restartedMain] c.e.common.config.SnowflakeConfig : 雪花算法配置完成,使用机器ID: 652
2025-07-16T09:48:22.860+08:00 INFO 59407 --- [restartedMain] c.e.common.util.SnowflakeIdGenerator : 雪花算法ID生成器初始化完成,机器ID: 652
2025-07-16T09:48:23.723+08:00 DEBUG 59407 --- [restartedMain] c.e.u.security.JwtAuthenticationFilter : Filter 'jwtAuthenticationFilter' configured for use
2025-07-16T09:48:23.726+08:00 DEBUG 59407 --- [restartedMain] o.s.w.f.ServerHttpObservationFilter : Filter 'serverHttpObservationFilter' configured for use
2025-07-16T09:48:24.530+08:00 DEBUG 59407 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : 19 mappings in 'requestMappingHandlerMapping'
2025-07-16T09:48:24.598+08:00 DEBUG 59407 --- [restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2025-07-16T09:48:24.845+08:00 INFO 59407 --- [restartedMain] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoint(s) beneath base path '/actuator'
2025-07-16T09:48:24.941+08:00 INFO 59407 --- [restartedMain] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@45cfcad, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@c053d77, org.springframework.security.web.context.SecurityContextHolderFilter@45f2ef0, org.springframework.security.web.header.HeaderWriterFilter@6ae52284, org.springframework.web.filter.CorsFilter@78f6eda5, org.springframework.security.web.authentication.logout.LogoutFilter@333894cb, com.emotionmuseum.user.security.JwtAuthenticationFilter@5e26f22e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@75d7738, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6c5ba2aa, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4dfa0a5, org.springframework.security.web.session.SessionManagementFilter@5d5c7ca2, org.springframework.security.web.access.ExceptionTranslationFilter@25061101, org.springframework.security.web.access.intercept.AuthorizationFilter@14938851]
2025-07-16T09:48:25.117+08:00 DEBUG 59407 --- [restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2025-07-16T09:48:25.165+08:00 DEBUG 59407 --- [restartedMain] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2025-07-16T09:48:25.472+08:00 INFO 59407 --- [restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2025-07-16T09:48:25.568+08:00 INFO 59407 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 19001 (http) with context path ''
2025-07-16T09:48:25.592+08:00 INFO 59407 --- [restartedMain] com.emotionmuseum.user.UserApplication : Started UserApplication in 7.306 seconds (process running for 7.799)
2025-07-16T09:49:18.783+08:00 INFO 59407 --- [http-nio-19001-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-07-16T09:49:18.783+08:00 INFO 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-07-16T09:49:18.783+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected StandardServletMultipartResolver
2025-07-16T09:49:18.783+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected AcceptHeaderLocaleResolver
2025-07-16T09:49:18.783+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected FixedThemeResolver
2025-07-16T09:49:18.785+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@10bf8750
2025-07-16T09:49:18.785+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.support.SessionFlashMapManager@a8815ac
2025-07-16T09:49:18.785+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2025-07-16T09:49:18.786+08:00 INFO 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
2025-07-16T09:49:18.845+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : GET "/actuator/health", parameters={}
2025-07-16T09:49:18.877+08:00 INFO 59407 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-07-16T09:49:19.066+08:00 INFO 59407 --- [http-nio-19001-exec-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@22c2045e
2025-07-16T09:49:19.069+08:00 INFO 59407 --- [http-nio-19001-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-07-16T09:49:19.516+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/vnd.spring-boot.actuator.v3+json', given [*/*] and supported [application/vnd.spring-boot.actuator.v3+json, application/vnd.spring-boot.actuator.v2+json, application/json]
2025-07-16T09:49:19.525+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.springframework.boot.actuate.health.SystemHealth@5e85c0c]
2025-07-16T09:49:19.539+08:00 DEBUG 59407 --- [http-nio-19001-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2025-07-16T09:49:31.721+08:00 WARN 59407 --- [Thread-4] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2025-07-16T09:49:31.721+08:00 WARN 59407 --- [Thread-10] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Start destroying Publisher
2025-07-16T09:49:31.721+08:00 WARN 59407 --- [Thread-10] c.a.nacos.common.notify.NotifyCenter : [NotifyCenter] Destruction of the end
2025-07-16T09:49:31.722+08:00 WARN 59407 --- [Thread-4] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Destruction of the end
2025-07-16T09:49:31.740+08:00 INFO 59407 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-07-16T09:49:31.746+08:00 INFO 59407 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
@@ -0,0 +1,53 @@
package com.emotionmuseum.user.config;
import com.emotionmuseum.user.security.UserDetailsServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 认证配置类
* 独立的认证配置,避免循环依赖
*
* @author emotion-museum
* @since 2025-07-15
*/
@Configuration
@RequiredArgsConstructor
public class AuthenticationConfig {
private final UserDetailsServiceImpl userDetailsService;
/**
* 密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 认证提供者
*/
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
/**
* 认证管理器
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
@@ -24,7 +24,6 @@ public class CaptchaConfig {
public Captcha arithmeticCaptcha() {
ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
captcha.setLen(2); // 几位数运算,默认是两位
captcha.getArithmeticString(); // 获取运算的公式:3+2=?
return captcha;
}
@@ -3,19 +3,17 @@ package com.emotionmuseum.user.config;
import com.emotionmuseum.user.security.JwtAuthenticationFilter;
import com.emotionmuseum.user.security.UserDetailsServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
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.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
@@ -37,35 +35,10 @@ import java.util.Collections;
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final ApplicationContext applicationContext;
private final AuthenticationProvider authenticationProvider;
/**
* 密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 认证提供者
*/
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
/**
* 认证管理器
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
/**
* CORS配置
@@ -126,10 +99,11 @@ public class SecurityConfig {
.anyRequest().authenticated())
// 配置认证提供者
.authenticationProvider(authenticationProvider())
.authenticationProvider(authenticationProvider)
// 添加JWT过滤器
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(applicationContext.getBean(JwtAuthenticationFilter.class),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@@ -1,66 +0,0 @@
package com.emotionmuseum.user.controller;
import com.emotionmuseum.common.result.Result;
import com.emotionmuseum.user.dto.CaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaVerifyRequest;
import com.emotionmuseum.user.service.CaptchaService;
import com.emotionmuseum.user.service.SliderCaptchaService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 验证码控制器
*
* @author emotion-museum
* @since 2025-07-15
*/
@Slf4j
@RestController
@RequestMapping("/captcha")
@RequiredArgsConstructor
@Tag(name = "验证码管理", description = "验证码生成和验证接口")
public class CaptchaController {
private final CaptchaService captchaService;
private final SliderCaptchaService sliderCaptchaService;
@Operation(summary = "生成验证码")
@GetMapping("/generate")
public Result<CaptchaResponse> generateCaptcha(
@Parameter(description = "验证码类型", example = "arithmetic") @RequestParam(defaultValue = "arithmetic") String type) {
log.info("生成验证码请求,类型: {}", type);
CaptchaResponse response = captchaService.generateCaptcha(type);
return Result.success(response);
}
@Operation(summary = "验证验证码")
@PostMapping("/verify")
public Result<Boolean> verifyCaptcha(
@Parameter(description = "验证码ID") @RequestParam String captchaId,
@Parameter(description = "验证码") @RequestParam String captcha) {
log.info("验证验证码请求,ID: {}", captchaId);
boolean isValid = captchaService.verifyCaptcha(captchaId, captcha);
return Result.success(isValid);
}
@Operation(summary = "生成滑块验证码")
@GetMapping("/slider/generate")
public Result<SliderCaptchaResponse> generateSliderCaptcha() {
log.info("生成滑块验证码请求");
SliderCaptchaResponse response = sliderCaptchaService.generateSliderCaptcha();
return Result.success(response);
}
@Operation(summary = "验证滑块验证码")
@PostMapping("/slider/verify")
public Result<Boolean> verifySliderCaptcha(@RequestBody SliderCaptchaVerifyRequest request) {
log.info("验证滑块验证码请求,ID: {}", request.getCaptchaId());
boolean isValid = sliderCaptchaService.verifySliderCaptcha(request);
return Result.success(isValid);
}
}
@@ -1,59 +0,0 @@
package com.emotionmuseum.user.controller;
import com.emotionmuseum.common.result.Result;
import com.emotionmuseum.user.dto.OAuthLoginRequest;
import com.emotionmuseum.user.service.OAuthService;
import com.emotionmuseum.user.vo.LoginResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
/**
* 第三方登录控制器
*
* @author emotion-museum
* @since 2025-07-15
*/
@Slf4j
@RestController
@RequestMapping("/oauth")
@RequiredArgsConstructor
@Tag(name = "第三方登录", description = "微信、QQ等第三方登录接口")
public class OAuthController {
private final OAuthService oauthService;
@Operation(summary = "获取第三方登录授权URL")
@GetMapping("/auth-url/{platform}")
public Result<String> getAuthUrl(
@Parameter(description = "平台类型", example = "wechat")
@PathVariable String platform) {
log.info("获取第三方登录授权URL: {}", platform);
String authUrl = oauthService.getAuthUrl(platform);
return Result.success(authUrl);
}
@Operation(summary = "第三方登录")
@PostMapping("/login")
public Result<LoginResponse> oauthLogin(@Valid @RequestBody OAuthLoginRequest request) {
log.info("第三方登录请求: {}", request.getPlatform());
LoginResponse response = oauthService.oauthLogin(request);
return Result.success(response);
}
@Operation(summary = "获取第三方用户信息")
@GetMapping("/user-info/{platform}")
public Result<Object> getOAuthUserInfo(
@Parameter(description = "平台类型") @PathVariable String platform,
@Parameter(description = "授权码") @RequestParam String code,
@Parameter(description = "状态码") @RequestParam(required = false) String state) {
log.info("获取第三方用户信息: {}", platform);
Object userInfo = oauthService.getOAuthUserInfo(platform, code, state);
return Result.success(userInfo);
}
}
@@ -1,11 +1,8 @@
package com.emotionmuseum.user.controller;
import com.emotionmuseum.common.result.Result;
import com.emotionmuseum.user.dto.LoginRequest;
import com.emotionmuseum.user.dto.RegisterRequest;
import com.emotionmuseum.user.dto.UserUpdateRequest;
import com.emotionmuseum.user.service.UserService;
import com.emotionmuseum.user.vo.LoginResponse;
import com.emotionmuseum.user.vo.UserInfoResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -28,36 +25,11 @@ import jakarta.validation.Valid;
@RequestMapping("/user")
@RequiredArgsConstructor
@Validated
@Tag(name = "用户管理", description = "用户注册、登录、信息管理")
@Tag(name = "用户管理", description = "用户信息管理")
public class UserController {
private final UserService userService;
@Operation(summary = "用户注册")
@PostMapping("/register")
public Result<UserInfoResponse> register(@Valid @RequestBody RegisterRequest request) {
log.info("用户注册请求: {}", request.getAccount());
UserInfoResponse response = userService.register(request);
return Result.success("注册成功", response);
}
@Operation(summary = "用户登录")
@PostMapping("/login")
public Result<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
log.info("用户登录请求: {}", request.getAccount());
LoginResponse response = userService.login(request);
return Result.success("登录成功", response);
}
@Operation(summary = "刷新Token")
@PostMapping("/refresh")
public Result<LoginResponse> refreshToken(
@Parameter(description = "刷新Token") @RequestParam String refreshToken) {
log.info("刷新Token请求");
LoginResponse response = userService.refreshToken(refreshToken);
return Result.success("Token刷新成功", response);
}
@Operation(summary = "获取用户信息")
@GetMapping("/info/{userId}")
public Result<UserInfoResponse> getUserInfo(
@@ -77,30 +49,6 @@ public class UserController {
return Result.success("更新成功", response);
}
@Operation(summary = "检查账号是否存在")
@GetMapping("/check/account")
public Result<Boolean> checkAccount(
@Parameter(description = "账号") @RequestParam String account) {
boolean exists = userService.existsByAccount(account);
return Result.success(exists);
}
@Operation(summary = "检查邮箱是否存在")
@GetMapping("/check/email")
public Result<Boolean> checkEmail(
@Parameter(description = "邮箱") @RequestParam String email) {
boolean exists = userService.existsByEmail(email);
return Result.success(exists);
}
@Operation(summary = "检查手机号是否存在")
@GetMapping("/check/phone")
public Result<Boolean> checkPhone(
@Parameter(description = "手机号") @RequestParam String phone) {
boolean exists = userService.existsByPhone(phone);
return Result.success(exists);
}
@Operation(summary = "更新最后活跃时间")
@PostMapping("/active/{userId}")
public Result<Void> updateLastActiveTime(
@@ -108,13 +56,4 @@ public class UserController {
userService.updateLastActiveTime(userId);
return Result.success();
}
@Operation(summary = "用户登出")
@PostMapping("/logout/{userId}")
public Result<Void> logout(
@Parameter(description = "用户ID") @PathVariable String userId) {
log.info("用户登出请求: {}", userId);
userService.logout(userId);
return Result.success();
}
}
@@ -1,31 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 验证码响应
*
* @author emotion-museum
* @since 2025-07-15
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "验证码响应")
public class CaptchaResponse {
@Schema(description = "验证码ID")
private String captchaId;
@Schema(description = "验证码图片Base64")
private String captchaImage;
@Schema(description = "验证码类型", example = "arithmetic")
private String captchaType;
@Schema(description = "过期时间(秒)", example = "300")
private Long expireTime;
}
@@ -1,36 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
/**
* 用户登录请求
*
* @author emotion-museum
* @since 2025-07-12
*/
@Data
@Schema(description = "用户登录请求")
public class LoginRequest {
@Schema(description = "账号(支持账号/邮箱/手机号)", example = "test_user")
@NotBlank(message = "账号不能为空")
private String account;
@Schema(description = "密码", example = "123456")
@NotBlank(message = "密码不能为空")
private String password;
@Schema(description = "验证码ID", example = "captcha_123")
@NotBlank(message = "验证码ID不能为空")
private String captchaId;
@Schema(description = "验证码", example = "1234")
@NotBlank(message = "验证码不能为空")
private String captcha;
@Schema(description = "记住我", example = "false")
private Boolean rememberMe = false;
}
@@ -1,36 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
/**
* 第三方登录请求
*
* @author emotion-museum
* @since 2025-07-15
*/
@Data
@Schema(description = "第三方登录请求")
public class OAuthLoginRequest {
@Schema(description = "第三方平台类型", example = "wechat")
@NotBlank(message = "平台类型不能为空")
private String platform;
@Schema(description = "授权码", example = "auth_code_123")
@NotBlank(message = "授权码不能为空")
private String code;
@Schema(description = "状态码", example = "state_123")
private String state;
@Schema(description = "验证码ID", example = "captcha_123")
@NotBlank(message = "验证码ID不能为空")
private String captchaId;
@Schema(description = "验证码", example = "1234")
@NotBlank(message = "验证码不能为空")
private String captcha;
}
@@ -1,78 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.*;
import java.time.LocalDate;
/**
* 用户注册请求
*
* @author emotion-museum
* @since 2025-07-12
*/
@Data
@Schema(description = "用户注册请求")
public class RegisterRequest {
@Schema(description = "账号", example = "test_user")
@NotBlank(message = "账号不能为空")
@Size(min = 4, max = 20, message = "账号长度必须在4-20位之间")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "账号只能包含字母、数字和下划线")
private String account;
@Schema(description = "密码", example = "123456")
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须在6-20位之间")
private String password;
@Schema(description = "确认密码", example = "123456")
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
@Schema(description = "验证码ID", example = "captcha_123")
@NotBlank(message = "验证码ID不能为空")
private String captchaId;
@Schema(description = "验证码", example = "1234")
@NotBlank(message = "验证码不能为空")
private String captcha;
@Schema(description = "用户名", example = "测试用户")
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2-20位之间")
private String username;
@Schema(description = "邮箱", example = "test@example.com")
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@Schema(description = "手机号", example = "13800138000")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@Schema(description = "昵称", example = "小测试")
@NotBlank(message = "昵称不能为空")
@Size(min = 1, max = 20, message = "昵称长度必须在1-20位之间")
private String nickname;
@Schema(description = "生日", example = "1990-01-01")
private LocalDate birthDate;
@Schema(description = "所在地", example = "北京市")
@Size(max = 50, message = "所在地长度不能超过50位")
private String location;
@Schema(description = "个人简介", example = "这是一个测试用户")
@Size(max = 200, message = "个人简介长度不能超过200位")
private String bio;
/**
* 验证密码一致性
*/
public boolean isPasswordMatch() {
return password != null && password.equals(confirmPassword);
}
}
@@ -1,37 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 滑块验证码响应
*
* @author emotion-museum
* @since 2025-07-15
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "滑块验证码响应")
public class SliderCaptchaResponse {
@Schema(description = "验证码ID")
private String captchaId;
@Schema(description = "背景图片Base64")
private String backgroundImage;
@Schema(description = "滑块图片Base64")
private String sliderImage;
@Schema(description = "滑块X坐标")
private Integer sliderX;
@Schema(description = "滑块Y坐标")
private Integer sliderY;
@Schema(description = "过期时间(秒)")
private Long expireTime;
}
@@ -1,33 +0,0 @@
package com.emotionmuseum.user.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* 滑块验证码验证请求
*
* @author emotion-museum
* @since 2025-07-15
*/
@Data
@Schema(description = "滑块验证码验证请求")
public class SliderCaptchaVerifyRequest {
@Schema(description = "验证码ID")
@NotBlank(message = "验证码ID不能为空")
private String captchaId;
@Schema(description = "滑块X坐标")
@NotNull(message = "滑块X坐标不能为空")
private Integer x;
@Schema(description = "滑块Y坐标")
@NotNull(message = "滑块Y坐标不能为空")
private Integer y;
@Schema(description = "滑动轨迹")
private String track;
}
@@ -14,30 +14,6 @@ import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 根据账号查询用户
*
* @param account 账号
* @return 用户信息
*/
User selectByAccount(@Param("account") String account);
/**
* 根据邮箱查询用户
*
* @param email 邮箱
* @return 用户信息
*/
User selectByEmail(@Param("email") String email);
/**
* 根据手机号查询用户
*
* @param phone 手机号
* @return 用户信息
*/
User selectByPhone(@Param("phone") String phone);
/**
* 更新最后活跃时间
*
@@ -8,6 +8,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Lazy
private final UserDetailsService userDetailsService;
private final RedisTemplate<String, Object> redisTemplate;
@@ -4,6 +4,7 @@ import com.emotionmuseum.user.entity.User;
import com.emotionmuseum.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@@ -25,6 +26,7 @@ import java.util.Collections;
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
@Lazy
private final UserService userService;
@Override
@@ -1,36 +0,0 @@
package com.emotionmuseum.user.service;
import com.emotionmuseum.user.dto.CaptchaResponse;
/**
* 验证码服务接口
*
* @author emotion-museum
* @since 2025-07-15
*/
public interface CaptchaService {
/**
* 生成验证码
*
* @param type 验证码类型 (arithmetic, chinese, gif, spec)
* @return 验证码响应
*/
CaptchaResponse generateCaptcha(String type);
/**
* 验证验证码
*
* @param captchaId 验证码ID
* @param captcha 用户输入的验证码
* @return 是否验证成功
*/
boolean verifyCaptcha(String captchaId, String captcha);
/**
* 删除验证码
*
* @param captchaId 验证码ID
*/
void removeCaptcha(String captchaId);
}
@@ -1,39 +0,0 @@
package com.emotionmuseum.user.service;
import com.emotionmuseum.user.dto.OAuthLoginRequest;
import com.emotionmuseum.user.vo.LoginResponse;
/**
* 第三方登录服务接口
*
* @author emotion-museum
* @since 2025-07-15
*/
public interface OAuthService {
/**
* 获取第三方登录授权URL
*
* @param platform 平台类型 (wechat, qq, wechat-mp)
* @return 授权URL
*/
String getAuthUrl(String platform);
/**
* 第三方登录
*
* @param request 第三方登录请求
* @return 登录响应
*/
LoginResponse oauthLogin(OAuthLoginRequest request);
/**
* 获取第三方用户信息
*
* @param platform 平台类型
* @param code 授权码
* @param state 状态码
* @return 用户信息
*/
Object getOAuthUserInfo(String platform, String code, String state);
}
@@ -1,35 +0,0 @@
package com.emotionmuseum.user.service;
import com.emotionmuseum.user.dto.SliderCaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaVerifyRequest;
/**
* 滑块验证码服务接口
*
* @author emotion-museum
* @since 2025-07-15
*/
public interface SliderCaptchaService {
/**
* 生成滑块验证码
*
* @return 滑块验证码响应
*/
SliderCaptchaResponse generateSliderCaptcha();
/**
* 验证滑块验证码
*
* @param request 验证请求
* @return 是否验证成功
*/
boolean verifySliderCaptcha(SliderCaptchaVerifyRequest request);
/**
* 删除滑块验证码
*
* @param captchaId 验证码ID
*/
void removeSliderCaptcha(String captchaId);
}
@@ -1,11 +1,8 @@
package com.emotionmuseum.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotionmuseum.user.dto.LoginRequest;
import com.emotionmuseum.user.dto.RegisterRequest;
import com.emotionmuseum.user.dto.UserUpdateRequest;
import com.emotionmuseum.user.entity.User;
import com.emotionmuseum.user.vo.LoginResponse;
import com.emotionmuseum.user.vo.UserInfoResponse;
/**
@@ -16,30 +13,6 @@ import com.emotionmuseum.user.vo.UserInfoResponse;
*/
public interface UserService extends IService<User> {
/**
* 用户注册
*
* @param request 注册请求
* @return 用户信息
*/
UserInfoResponse register(RegisterRequest request);
/**
* 用户登录
*
* @param request 登录请求
* @return 登录响应
*/
LoginResponse login(LoginRequest request);
/**
* 刷新Token
*
* @param refreshToken 刷新Token
* @return 登录响应
*/
LoginResponse refreshToken(String refreshToken);
/**
* 根据用户ID获取用户信息
*
@@ -57,49 +30,10 @@ public interface UserService extends IService<User> {
*/
UserInfoResponse updateUserInfo(String userId, UserUpdateRequest request);
/**
* 检查账号是否存在
*
* @param account 账号
* @return 是否存在
*/
boolean existsByAccount(String account);
/**
* 检查邮箱是否存在
*
* @param email 邮箱
* @return 是否存在
*/
boolean existsByEmail(String email);
/**
* 检查手机号是否存在
*
* @param phone 手机号
* @return 是否存在
*/
boolean existsByPhone(String phone);
/**
* 更新最后活跃时间
*
* @param userId 用户ID
*/
void updateLastActiveTime(String userId);
/**
* 用户登出
*
* @param userId 用户ID
*/
void logout(String userId);
/**
* 根据第三方平台ID查找用户
*
* @param thirdPartyId 第三方平台ID
* @return 用户信息
*/
User findByThirdPartyId(String thirdPartyId);
}
@@ -1,119 +0,0 @@
package com.emotionmuseum.user.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.emotionmuseum.user.dto.CaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaVerifyRequest;
import com.emotionmuseum.user.service.CaptchaService;
import com.emotionmuseum.user.service.SliderCaptchaService;
import com.wf.captcha.base.Captcha;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* 验证码服务实现
*
* @author emotion-museum
* @since 2025-07-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class CaptchaServiceImpl implements CaptchaService {
private final RedisTemplate<String, Object> redisTemplate;
private final ApplicationContext applicationContext;
private static final String CAPTCHA_KEY_PREFIX = "captcha:";
private static final long CAPTCHA_EXPIRE_TIME = 300; // 5分钟
@Override
public CaptchaResponse generateCaptcha(String type) {
try {
// 根据类型获取验证码Bean
String beanName = getBeanNameByType(type);
Captcha captcha = (Captcha) applicationContext.getBean(beanName);
// 生成验证码
String captchaId = IdUtil.simpleUUID();
String captchaText = captcha.text();
String captchaImage = captcha.toBase64();
// 存储到Redis
String redisKey = CAPTCHA_KEY_PREFIX + captchaId;
redisTemplate.opsForValue().set(redisKey, captchaText.toLowerCase(), CAPTCHA_EXPIRE_TIME, TimeUnit.SECONDS);
log.debug("生成验证码成功,ID: {}, 内容: {}", captchaId, captchaText);
return new CaptchaResponse(captchaId, captchaImage, type, CAPTCHA_EXPIRE_TIME);
} catch (Exception e) {
log.error("生成验证码失败: {}", e.getMessage());
throw new RuntimeException("生成验证码失败");
}
}
@Override
public boolean verifyCaptcha(String captchaId, String captcha) {
if (StrUtil.isBlank(captchaId) || StrUtil.isBlank(captcha)) {
return false;
}
try {
String redisKey = CAPTCHA_KEY_PREFIX + captchaId;
String storedCaptcha = (String) redisTemplate.opsForValue().get(redisKey);
if (StrUtil.isBlank(storedCaptcha)) {
log.warn("验证码已过期或不存在,ID: {}", captchaId);
return false;
}
// 验证码不区分大小写
boolean isValid = storedCaptcha.equalsIgnoreCase(captcha.trim());
if (isValid) {
// 验证成功后删除验证码
redisTemplate.delete(redisKey);
log.debug("验证码验证成功,ID: {}", captchaId);
} else {
log.warn("验证码验证失败,ID: {}, 期望: {}, 实际: {}", captchaId, storedCaptcha, captcha);
}
return isValid;
} catch (Exception e) {
log.error("验证验证码失败: {}", e.getMessage());
return false;
}
}
@Override
public void removeCaptcha(String captchaId) {
if (StrUtil.isNotBlank(captchaId)) {
String redisKey = CAPTCHA_KEY_PREFIX + captchaId;
redisTemplate.delete(redisKey);
log.debug("删除验证码,ID: {}", captchaId);
}
}
/**
* 根据类型获取Bean名称
*/
private String getBeanNameByType(String type) {
String defaultType = StrUtil.blankToDefault(type, "spec");
switch (defaultType) {
case "arithmetic":
return "arithmeticCaptcha";
case "chinese":
return "chineseCaptcha";
case "gif":
return "gifCaptcha";
default:
return "specCaptcha";
}
}
}
@@ -1,182 +0,0 @@
package com.emotionmuseum.user.service.impl;
import cn.hutool.core.util.StrUtil;
import com.emotionmuseum.common.result.ResultCode;
import com.emotionmuseum.common.util.JwtUtil;
import com.emotionmuseum.user.dto.OAuthLoginRequest;
import com.emotionmuseum.user.entity.User;
import com.emotionmuseum.user.service.CaptchaService;
import com.emotionmuseum.user.service.OAuthService;
import com.emotionmuseum.user.service.UserService;
import com.emotionmuseum.user.vo.LoginResponse;
import com.emotionmuseum.user.vo.UserInfoResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWeChatMpRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
/**
* 第三方登录服务实现
*
* @author emotion-museum
* @since 2025-07-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OAuthServiceImpl implements OAuthService {
private final ApplicationContext applicationContext;
private final CaptchaService captchaService;
private final UserService userService;
private final JwtUtil jwtUtil;
private final RedisTemplate<String, Object> redisTemplate;
private static final String REDIS_TOKEN_KEY_PREFIX = "auth:token:";
@Override
public String getAuthUrl(String platform) {
try {
AuthRequest authRequest = getAuthRequest(platform);
return authRequest.authorize();
} catch (Exception e) {
log.error("获取第三方登录授权URL失败: {}", e.getMessage());
throw new RuntimeException("获取授权URL失败");
}
}
@Override
public LoginResponse oauthLogin(OAuthLoginRequest request) {
// 验证验证码
if (!captchaService.verifyCaptcha(request.getCaptchaId(), request.getCaptcha())) {
throw new RuntimeException(ResultCode.CAPTCHA_ERROR.getMessage());
}
try {
// 获取第三方用户信息
AuthUser authUser = (AuthUser) getOAuthUserInfo(request.getPlatform(), request.getCode(),
request.getState());
if (authUser == null) {
throw new RuntimeException("获取第三方用户信息失败");
}
// 查找或创建用户
User user = findOrCreateUser(authUser, request.getPlatform());
// 生成Token
String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());
// 将token存储到Redis中
String redisKey = REDIS_TOKEN_KEY_PREFIX + user.getId();
redisTemplate.opsForValue().set(redisKey, accessToken, 24, TimeUnit.HOURS);
// 更新最后活跃时间
userService.updateLastActiveTime(user.getId());
// 构建响应
LoginResponse response = new LoginResponse();
response.setAccessToken(accessToken);
response.setRefreshToken(refreshToken);
response.setExpiresIn(86400L); // 24小时
response.setUserInfo(convertToUserInfoResponse(user));
response.setLoginTime(LocalDateTime.now());
log.info("第三方登录成功: {} - {}", request.getPlatform(), user.getId());
return response;
} catch (Exception e) {
log.error("第三方登录失败: {}", e.getMessage());
throw new RuntimeException("第三方登录失败: " + e.getMessage());
}
}
@Override
public Object getOAuthUserInfo(String platform, String code, String state) {
try {
AuthRequest authRequest = getAuthRequest(platform);
AuthCallback callback = AuthCallback.builder()
.code(code)
.state(state)
.build();
AuthResponse<AuthUser> response = authRequest.login(callback);
if (response.ok()) {
return response.getData();
} else {
log.error("第三方登录失败: {}", response.getMsg());
throw new RuntimeException("第三方登录失败: " + response.getMsg());
}
} catch (Exception e) {
log.error("获取第三方用户信息失败: {}", e.getMessage());
throw new RuntimeException("获取第三方用户信息失败");
}
}
/**
* 根据平台类型获取AuthRequest
*/
private AuthRequest getAuthRequest(String platform) {
switch (platform.toLowerCase()) {
case "wechat":
return applicationContext.getBean(AuthWeChatOpenRequest.class);
case "wechat-mp":
return applicationContext.getBean(AuthWeChatMpRequest.class);
case "qq":
return applicationContext.getBean(AuthQqRequest.class);
default:
throw new RuntimeException("不支持的第三方平台: " + platform);
}
}
/**
* 查找或创建用户
*/
private User findOrCreateUser(AuthUser authUser, String platform) {
// 根据第三方平台ID查找用户
String thirdPartyId = platform + "_" + authUser.getUuid();
User existingUser = userService.findByThirdPartyId(thirdPartyId);
if (existingUser != null) {
return existingUser;
}
// 创建新用户
User newUser = new User();
newUser.setUsername(authUser.getNickname());
newUser.setNickname(authUser.getNickname());
newUser.setAvatar(authUser.getAvatar());
newUser.setEmail(authUser.getEmail());
newUser.setThirdPartyId(thirdPartyId);
newUser.setThirdPartyType(platform);
newUser.setStatus(1); // 启用状态
// 保存用户
userService.save(newUser);
log.info("创建第三方登录用户: {} - {}", platform, newUser.getId());
return newUser;
}
/**
* 转换为用户信息响应
*/
private UserInfoResponse convertToUserInfoResponse(User user) {
UserInfoResponse response = new UserInfoResponse();
BeanUtils.copyProperties(user, response);
return response;
}
}
@@ -1,210 +0,0 @@
package com.emotionmuseum.user.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.emotionmuseum.user.dto.SliderCaptchaResponse;
import com.emotionmuseum.user.dto.SliderCaptchaVerifyRequest;
import com.emotionmuseum.user.service.SliderCaptchaService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* 滑块验证码服务实现
*
* @author emotion-museum
* @since 2025-07-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SliderCaptchaServiceImpl implements SliderCaptchaService {
private final RedisTemplate<String, Object> redisTemplate;
private static final String SLIDER_CAPTCHA_KEY_PREFIX = "slider_captcha:";
private static final long SLIDER_CAPTCHA_EXPIRE_TIME = 300; // 5分钟
private static final int BACKGROUND_WIDTH = 300;
private static final int BACKGROUND_HEIGHT = 150;
private static final int SLIDER_WIDTH = 60;
private static final int SLIDER_HEIGHT = 60;
private static final int TOLERANCE = 5; // 容错范围
@Override
public SliderCaptchaResponse generateSliderCaptcha() {
try {
String captchaId = IdUtil.simpleUUID();
// 生成随机位置
Random random = new Random();
int sliderX = random.nextInt(BACKGROUND_WIDTH - SLIDER_WIDTH - 50) + 50;
int sliderY = random.nextInt(BACKGROUND_HEIGHT - SLIDER_HEIGHT - 20) + 20;
// 生成背景图片
BufferedImage backgroundImage = generateBackgroundImage(sliderX, sliderY);
String backgroundBase64 = imageToBase64(backgroundImage);
// 生成滑块图片
BufferedImage sliderImage = generateSliderImage();
String sliderBase64 = imageToBase64(sliderImage);
// 存储到Redis
String redisKey = SLIDER_CAPTCHA_KEY_PREFIX + captchaId;
SliderCaptchaData data = new SliderCaptchaData(sliderX, sliderY);
redisTemplate.opsForValue().set(redisKey, data, SLIDER_CAPTCHA_EXPIRE_TIME, TimeUnit.SECONDS);
log.debug("生成滑块验证码成功,ID: {}, 位置: ({}, {})", captchaId, sliderX, sliderY);
return new SliderCaptchaResponse(captchaId, backgroundBase64, sliderBase64, 0, sliderY, SLIDER_CAPTCHA_EXPIRE_TIME);
} catch (Exception e) {
log.error("生成滑块验证码失败: {}", e.getMessage());
throw new RuntimeException("生成滑块验证码失败");
}
}
@Override
public boolean verifySliderCaptcha(SliderCaptchaVerifyRequest request) {
if (StrUtil.isBlank(request.getCaptchaId()) || request.getX() == null) {
return false;
}
try {
String redisKey = SLIDER_CAPTCHA_KEY_PREFIX + request.getCaptchaId();
SliderCaptchaData data = (SliderCaptchaData) redisTemplate.opsForValue().get(redisKey);
if (data == null) {
log.warn("滑块验证码已过期或不存在,ID: {}", request.getCaptchaId());
return false;
}
// 验证X坐标是否在容错范围内
boolean isValid = Math.abs(data.getSliderX() - request.getX()) <= TOLERANCE;
if (isValid) {
// 验证成功后删除验证码
redisTemplate.delete(redisKey);
log.debug("滑块验证码验证成功,ID: {}", request.getCaptchaId());
} else {
log.warn("滑块验证码验证失败,ID: {}, 期望X: {}, 实际X: {}",
request.getCaptchaId(), data.getSliderX(), request.getX());
}
return isValid;
} catch (Exception e) {
log.error("验证滑块验证码失败: {}", e.getMessage());
return false;
}
}
@Override
public void removeSliderCaptcha(String captchaId) {
if (StrUtil.isNotBlank(captchaId)) {
String redisKey = SLIDER_CAPTCHA_KEY_PREFIX + captchaId;
redisTemplate.delete(redisKey);
log.debug("删除滑块验证码,ID: {}", captchaId);
}
}
/**
* 生成背景图片
*/
private BufferedImage generateBackgroundImage(int sliderX, int sliderY) {
BufferedImage image = new BufferedImage(BACKGROUND_WIDTH, BACKGROUND_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制渐变背景
GradientPaint gradient = new GradientPaint(0, 0, new Color(135, 206, 250),
BACKGROUND_WIDTH, BACKGROUND_HEIGHT, new Color(70, 130, 180));
g2d.setPaint(gradient);
g2d.fillRect(0, 0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT);
// 绘制一些装饰性图形
Random random = new Random();
g2d.setColor(new Color(255, 255, 255, 100));
for (int i = 0; i < 20; i++) {
int x = random.nextInt(BACKGROUND_WIDTH);
int y = random.nextInt(BACKGROUND_HEIGHT);
int size = random.nextInt(20) + 5;
g2d.fillOval(x, y, size, size);
}
// 绘制滑块缺口
g2d.setColor(new Color(0, 0, 0, 150));
g2d.fillRoundRect(sliderX, sliderY, SLIDER_WIDTH, SLIDER_HEIGHT, 10, 10);
g2d.dispose();
return image;
}
/**
* 生成滑块图片
*/
private BufferedImage generateSliderImage() {
BufferedImage image = new BufferedImage(SLIDER_WIDTH, SLIDER_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
// 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制滑块
g2d.setColor(new Color(70, 130, 180));
g2d.fillRoundRect(0, 0, SLIDER_WIDTH, SLIDER_HEIGHT, 10, 10);
// 绘制边框
g2d.setColor(new Color(255, 255, 255));
g2d.setStroke(new BasicStroke(2));
g2d.drawRoundRect(1, 1, SLIDER_WIDTH - 2, SLIDER_HEIGHT - 2, 10, 10);
// 绘制箭头
g2d.setColor(Color.WHITE);
int[] xPoints = {SLIDER_WIDTH/2 - 8, SLIDER_WIDTH/2 + 8, SLIDER_WIDTH/2};
int[] yPoints = {SLIDER_HEIGHT/2, SLIDER_HEIGHT/2, SLIDER_HEIGHT/2 - 8};
g2d.fillPolygon(xPoints, yPoints, 3);
g2d.dispose();
return image;
}
/**
* 图片转Base64
*/
private String imageToBase64(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", baos);
byte[] bytes = baos.toByteArray();
return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
}
/**
* 滑块验证码数据
*/
public static class SliderCaptchaData {
private int sliderX;
private int sliderY;
public SliderCaptchaData() {}
public SliderCaptchaData(int sliderX, int sliderY) {
this.sliderX = sliderX;
this.sliderY = sliderY;
}
public int getSliderX() { return sliderX; }
public void setSliderX(int sliderX) { this.sliderX = sliderX; }
public int getSliderY() { return sliderY; }
public void setSliderY(int sliderY) { this.sliderY = sliderY; }
}
}
@@ -1,30 +1,17 @@
package com.emotionmuseum.user.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotionmuseum.common.result.ResultCode;
import com.emotionmuseum.common.util.JwtUtil;
import com.emotionmuseum.user.dto.LoginRequest;
import com.emotionmuseum.user.dto.RegisterRequest;
import com.emotionmuseum.user.dto.UserUpdateRequest;
import com.emotionmuseum.user.entity.User;
import com.emotionmuseum.user.mapper.UserMapper;
import com.emotionmuseum.user.service.UserService;
import com.emotionmuseum.user.vo.LoginResponse;
import com.emotionmuseum.user.vo.UserInfoResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import com.emotionmuseum.user.service.CaptchaService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 用户服务实现类
*
@@ -36,156 +23,11 @@ import java.time.LocalDateTime;
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final JwtUtil jwtUtil;
private final RedisTemplate<String, Object> redisTemplate;
private final PasswordEncoder passwordEncoder;
private final CaptchaService captchaService;
private static final String REDIS_TOKEN_KEY_PREFIX = "auth:token:";
@Override
@Transactional(rollbackFor = Exception.class)
public UserInfoResponse register(RegisterRequest request) {
// 验证验证码
if (!captchaService.verifyCaptcha(request.getCaptchaId(), request.getCaptcha())) {
throw new RuntimeException(ResultCode.CAPTCHA_ERROR.getMessage());
}
// 验证密码一致性
if (!request.isPasswordMatch()) {
throw new RuntimeException(ResultCode.PARAM_VALIDATION_ERROR.getMessage() + ": 两次密码不一致");
}
// 检查账号是否存在
if (existsByAccount(request.getAccount())) {
throw new RuntimeException(ResultCode.ACCOUNT_ALREADY_EXISTS.getMessage());
}
// 检查邮箱是否存在
if (existsByEmail(request.getEmail())) {
throw new RuntimeException(ResultCode.EMAIL_ALREADY_EXISTS.getMessage());
}
// 检查手机号是否存在
if (StrUtil.isNotBlank(request.getPhone()) && existsByPhone(request.getPhone())) {
throw new RuntimeException(ResultCode.PHONE_ALREADY_EXISTS.getMessage());
}
// 创建用户
User user = new User();
BeanUtils.copyProperties(request, user);
// 加密密码
user.setPassword(passwordEncoder.encode(request.getPassword()));
// 设置默认值
user.setMemberLevel("free");
user.setTotalDays(0);
user.setSelfAwareness(new BigDecimal("50.00"));
user.setEmotionalResilience(new BigDecimal("50.00"));
user.setActionPower(new BigDecimal("50.00"));
user.setEmpathy(new BigDecimal("50.00"));
user.setLifeEnthusiasm(new BigDecimal("50.00"));
user.setStatus(1);
user.setIsVerified(0);
user.setLastActiveTime(LocalDateTime.now());
// 保存用户
save(user);
log.info("用户注册成功: {}", user.getAccount());
return convertToUserInfoResponse(user);
}
@Override
public LoginResponse login(LoginRequest request) {
// 验证验证码
if (!captchaService.verifyCaptcha(request.getCaptchaId(), request.getCaptcha())) {
throw new RuntimeException(ResultCode.CAPTCHA_ERROR.getMessage());
}
// 查找用户(支持账号/邮箱/手机号登录)
User user = findUserByAccount(request.getAccount());
if (user == null) {
throw new RuntimeException(ResultCode.USER_NOT_FOUND.getMessage());
}
// 验证密码
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new RuntimeException(ResultCode.INVALID_CREDENTIALS.getMessage());
}
// 检查用户状态
if (user.getStatus() == 0) {
throw new RuntimeException(ResultCode.USER_DISABLED.getMessage());
}
// 生成Token
String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());
// 将token存储到Redis中(用于登出和token管理)
String redisKey = REDIS_TOKEN_KEY_PREFIX + user.getId();
redisTemplate.opsForValue().set(redisKey, accessToken, 24, java.util.concurrent.TimeUnit.HOURS);
// 更新最后活跃时间
updateLastActiveTime(user.getId());
// 构建响应
LoginResponse response = new LoginResponse();
response.setAccessToken(accessToken);
response.setRefreshToken(refreshToken);
response.setExpiresIn(86400L); // 24小时
response.setUserInfo(convertToUserInfoResponse(user));
response.setLoginTime(LocalDateTime.now());
log.info("用户登录成功: {}", user.getAccount());
return response;
}
@Override
public LoginResponse refreshToken(String refreshToken) {
if (!jwtUtil.validateToken(refreshToken)) {
throw new RuntimeException(ResultCode.REFRESH_TOKEN_INVALID.getMessage());
}
String userId = jwtUtil.getUserIdFromToken(refreshToken);
String username = jwtUtil.getUsernameFromToken(refreshToken);
if (StrUtil.isBlank(userId) || StrUtil.isBlank(username)) {
throw new RuntimeException(ResultCode.REFRESH_TOKEN_INVALID.getMessage());
}
// 生成新Token
String newAccessToken = jwtUtil.generateToken(userId, username);
String newRefreshToken = jwtUtil.generateRefreshToken(userId, username);
// 更新Redis中的token
String redisKey = REDIS_TOKEN_KEY_PREFIX + userId;
redisTemplate.opsForValue().set(redisKey, newAccessToken, 24, java.util.concurrent.TimeUnit.HOURS);
// 获取用户信息
User user = getById(userId);
if (user == null) {
throw new RuntimeException(ResultCode.USER_NOT_FOUND.getMessage());
}
// 构建响应
LoginResponse response = new LoginResponse();
response.setAccessToken(newAccessToken);
response.setRefreshToken(newRefreshToken);
response.setExpiresIn(86400L);
response.setUserInfo(convertToUserInfoResponse(user));
response.setLoginTime(LocalDateTime.now());
return response;
}
@Override
public UserInfoResponse getUserInfo(String userId) {
User user = getById(userId);
if (user == null) {
throw new RuntimeException(ResultCode.USER_NOT_FOUND.getMessage());
throw new RuntimeException("用户不存在");
}
return convertToUserInfoResponse(user);
}
@@ -195,21 +37,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
public UserInfoResponse updateUserInfo(String userId, UserUpdateRequest request) {
User user = getById(userId);
if (user == null) {
throw new RuntimeException(ResultCode.USER_NOT_FOUND.getMessage());
}
// 检查邮箱是否被其他用户使用
if (StrUtil.isNotBlank(request.getEmail()) && !request.getEmail().equals(user.getEmail())) {
if (existsByEmail(request.getEmail())) {
throw new RuntimeException(ResultCode.EMAIL_ALREADY_EXISTS.getMessage());
}
}
// 检查手机号是否被其他用户使用
if (StrUtil.isNotBlank(request.getPhone()) && !request.getPhone().equals(user.getPhone())) {
if (existsByPhone(request.getPhone())) {
throw new RuntimeException(ResultCode.PHONE_ALREADY_EXISTS.getMessage());
}
throw new RuntimeException("用户不存在");
}
// 更新用户信息
@@ -220,76 +48,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return convertToUserInfoResponse(user);
}
@Override
public boolean existsByAccount(String account) {
return baseMapper.selectByAccount(account) != null;
}
@Override
public boolean existsByEmail(String email) {
return baseMapper.selectByEmail(email) != null;
}
@Override
public boolean existsByPhone(String phone) {
return StrUtil.isNotBlank(phone) && baseMapper.selectByPhone(phone) != null;
}
@Override
public void updateLastActiveTime(String userId) {
baseMapper.updateLastActiveTime(userId);
}
@Override
public void logout(String userId) {
// 从Redis中删除token
String redisKey = REDIS_TOKEN_KEY_PREFIX + userId;
redisTemplate.delete(redisKey);
log.info("用户登出成功: {}", userId);
}
@Override
public User findByThirdPartyId(String thirdPartyId) {
if (StrUtil.isBlank(thirdPartyId)) {
return null;
}
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getThirdPartyId, thirdPartyId);
queryWrapper.eq(User::getIsDeleted, 0);
return getOne(queryWrapper);
}
/**
* 根据账号查找用户(支持账号/邮箱/手机号)
*/
private User findUserByAccount(String account) {
// 先按账号查找
User user = baseMapper.selectByAccount(account);
if (user != null) {
return user;
}
// 按邮箱查找
if (account.contains("@")) {
user = baseMapper.selectByEmail(account);
if (user != null) {
return user;
}
}
// 按手机号查找
if (account.matches("^1[3-9]\\d{9}$")) {
user = baseMapper.selectByPhone(account);
if (user != null) {
return user;
}
}
return null;
}
/**
* 转换为用户信息响应
*/
@@ -1,37 +0,0 @@
package com.emotionmuseum.user.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 登录响应
*
* @author emotion-museum
* @since 2025-07-12
*/
@Data
@Schema(description = "登录响应")
public class LoginResponse {
@Schema(description = "访问Token")
private String accessToken;
@Schema(description = "刷新Token")
private String refreshToken;
@Schema(description = "Token类型", example = "Bearer")
private String tokenType = "Bearer";
@Schema(description = "Token过期时间(秒)", example = "86400")
private Long expiresIn;
@Schema(description = "用户信息")
private UserInfoResponse userInfo;
@Schema(description = "登录时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime loginTime;
}
@@ -1,102 +1,55 @@
server:
port: 19001
# 本地开发环境配置
spring:
application:
name: emotion-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: Peanut2817*#
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: Peanut2817*#
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
password:
database: 0
timeout: 10000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
# MyBatis Plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: isDeleted
logic-delete-value: 1
logic-not-delete-value: 0
# Nacos配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
register-enabled: true
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
# JWT配置
jwt:
secret: emotion-museum-secret-key-2025-local
expiration: 86400
refresh-expiration: 604800
# 第三方登录配置
oauth:
wechat:
client-id: ${WECHAT_CLIENT_ID:your_wechat_client_id}
client-secret: ${WECHAT_CLIENT_SECRET:your_wechat_client_secret}
redirect-uri: ${WECHAT_REDIRECT_URI:http://localhost:19001/oauth/callback/wechat}
wechat-mp:
client-id: ${WECHAT_MP_CLIENT_ID:your_wechat_mp_client_id}
client-secret: ${WECHAT_MP_CLIENT_SECRET:your_wechat_mp_client_secret}
redirect-uri: ${WECHAT_MP_REDIRECT_URI:http://localhost:19001/oauth/callback/wechat-mp}
qq:
client-id: ${QQ_CLIENT_ID:your_qq_client_id}
client-secret: ${QQ_CLIENT_SECRET:your_qq_client_secret}
redirect-uri: ${QQ_REDIRECT_URI:http://localhost:19001/oauth/callback/qq}
# 日志配置
logging:
level:
com.emotionmuseum: debug
org.springframework.security: debug
org.springframework.web: debug
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-user-local.log
# 管理端点配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
@@ -0,0 +1,55 @@
# 本地开发环境配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: nacos
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: nacos
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: debug
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-user-local.log
@@ -1,75 +1,55 @@
server:
port: 9001
# 生产环境配置
spring:
application:
name: emotion-user
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
namespace: public
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
enabled: true
ip: ${SERVER_IP:localhost}
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
environment: prod
zone: prod
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:emotion_museum}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: ${MYSQL_USERNAME:emotion}
password: ${MYSQL_PASSWORD:EmotionDB2024!}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000ms
# MyBatis Plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: input
logic-delete-field: is_deleted
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath*:mapper/*.xml
# 日志配置
logging:
level:
com.emotionmuseum: INFO
com.baomidou.mybatisplus: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 管理端点
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
com.emotionmuseum: warn
com.baomidou.mybatisplus: warn
com.alibaba.nacos: error
file:
name: logs/emotion-user-prod.log
@@ -0,0 +1,55 @@
# 测试环境配置
spring:
cloud:
nacos:
discovery:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
enabled: true
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
zone: test
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: info
com.baomidou.mybatisplus: info
com.alibaba.nacos: warn
file:
name: logs/emotion-user-test.log
@@ -4,40 +4,74 @@ server:
spring:
application:
name: emotion-user
# 配置文件激活
profiles:
active: dev
active: ${SPRING_PROFILES_ACTIVE:local}
# 允许Bean覆盖和循环引用
main:
allow-bean-definition-overriding: true
allow-circular-references: true
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:emotion_museum}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:123456}
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 600000
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
# Redis配置
data:
redis:
host: localhost
port: 6379
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
database: 0
timeout: 3000ms
timeout: 10000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000ms
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
# Nacos配置
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: emotion-dev
group: DEFAULT_GROUP
enabled: false
server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
enabled: ${NACOS_DISCOVERY_ENABLED:true}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
metadata:
version: 1.0.0
zone: ${NACOS_ZONE:default}
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
enabled: false
server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
file-extension: yml
enabled: ${NACOS_CONFIG_ENABLED:false}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
mybatis-plus:
configuration:
@@ -2,18 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.emotionmuseum.user.mapper.UserMapper">
<!-- 根据账号查询用户 -->
<select id="selectByAccount" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE account = #{account} AND is_deleted = 0 </select>
<!-- 根据邮箱查询用户 -->
<select id="selectByEmail" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE email = #{email} AND is_deleted = 0 </select>
<!-- 根据手机号查询用户 -->
<select id="selectByPhone" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE phone = #{phone} AND is_deleted = 0 </select>
<!-- 更新最后活跃时间 -->
<update id="updateLastActiveTime"> UPDATE user SET last_active_time = NOW(), update_time = NOW()
WHERE id = #{userId} AND is_deleted = 0 </update>
@@ -1,102 +1,55 @@
server:
port: 19001
# 本地开发环境配置
spring:
application:
name: emotion-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: Peanut2817*#
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: Peanut2817*#
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
password:
database: 0
timeout: 10000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
# MyBatis Plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: isDeleted
logic-delete-value: 1
logic-not-delete-value: 0
# Nacos配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
register-enabled: true
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
# JWT配置
jwt:
secret: emotion-museum-secret-key-2025-local
expiration: 86400
refresh-expiration: 604800
# 第三方登录配置
oauth:
wechat:
client-id: ${WECHAT_CLIENT_ID:your_wechat_client_id}
client-secret: ${WECHAT_CLIENT_SECRET:your_wechat_client_secret}
redirect-uri: ${WECHAT_REDIRECT_URI:http://localhost:19001/oauth/callback/wechat}
wechat-mp:
client-id: ${WECHAT_MP_CLIENT_ID:your_wechat_mp_client_id}
client-secret: ${WECHAT_MP_CLIENT_SECRET:your_wechat_mp_client_secret}
redirect-uri: ${WECHAT_MP_REDIRECT_URI:http://localhost:19001/oauth/callback/wechat-mp}
qq:
client-id: ${QQ_CLIENT_ID:your_qq_client_id}
client-secret: ${QQ_CLIENT_SECRET:your_qq_client_secret}
redirect-uri: ${QQ_REDIRECT_URI:http://localhost:19001/oauth/callback/qq}
# 日志配置
logging:
level:
com.emotionmuseum: debug
org.springframework.security: debug
org.springframework.web: debug
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-user-local.log
# 管理端点配置
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
@@ -0,0 +1,55 @@
# 本地开发环境配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: nacos
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: nacos
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: debug
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-user-local.log
@@ -1,75 +1,55 @@
server:
port: 9001
# 生产环境配置
spring:
application:
name: emotion-user
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
namespace: public
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
enabled: true
ip: ${SERVER_IP:localhost}
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
environment: prod
zone: prod
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:emotion_museum}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: ${MYSQL_USERNAME:emotion}
password: ${MYSQL_PASSWORD:EmotionDB2024!}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000ms
# MyBatis Plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: input
logic-delete-field: is_deleted
logic-delete-value: 1
logic-not-delete-value: 0
mapper-locations: classpath*:mapper/*.xml
# 日志配置
logging:
level:
com.emotionmuseum: INFO
com.baomidou.mybatisplus: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 管理端点
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
com.emotionmuseum: warn
com.baomidou.mybatisplus: warn
com.alibaba.nacos: error
file:
name: logs/emotion-user-prod.log
@@ -0,0 +1,55 @@
# 测试环境配置
spring:
cloud:
nacos:
discovery:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
enabled: true
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
zone: test
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: info
com.baomidou.mybatisplus: info
com.alibaba.nacos: warn
file:
name: logs/emotion-user-test.log
@@ -4,40 +4,74 @@ server:
spring:
application:
name: emotion-user
# 配置文件激活
profiles:
active: dev
active: ${SPRING_PROFILES_ACTIVE:local}
# 允许Bean覆盖和循环引用
main:
allow-bean-definition-overriding: true
allow-circular-references: true
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:emotion_museum}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:123456}
hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 600000
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
# Redis配置
data:
redis:
host: localhost
port: 6379
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
database: 0
timeout: 3000ms
timeout: 10000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000ms
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
# Nacos配置
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: emotion-dev
group: DEFAULT_GROUP
enabled: false
server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
enabled: ${NACOS_DISCOVERY_ENABLED:true}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
metadata:
version: 1.0.0
zone: ${NACOS_ZONE:default}
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
enabled: false
server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
file-extension: yml
enabled: ${NACOS_CONFIG_ENABLED:false}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
mybatis-plus:
configuration:
@@ -2,18 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.emotionmuseum.user.mapper.UserMapper">
<!-- 根据账号查询用户 -->
<select id="selectByAccount" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE account = #{account} AND is_deleted = 0 </select>
<!-- 根据邮箱查询用户 -->
<select id="selectByEmail" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE email = #{email} AND is_deleted = 0 </select>
<!-- 根据手机号查询用户 -->
<select id="selectByPhone" resultType="com.emotionmuseum.user.entity.User"> SELECT * FROM user
WHERE phone = #{phone} AND is_deleted = 0 </select>
<!-- 更新最后活跃时间 -->
<update id="updateLastActiveTime"> UPDATE user SET last_active_time = NOW(), update_time = NOW()
WHERE id = #{userId} AND is_deleted = 0 </update>
@@ -0,0 +1,3 @@
artifactId=emotion-user
groupId=com.emotionmuseum
version=1.0.0
@@ -1,32 +1,17 @@
com/emotionmuseum/user/vo/LoginResponse.class
com/emotionmuseum/user/security/UserDetailsServiceImpl$SecurityUser.class
com/emotionmuseum/user/config/SecurityConfig.class
com/emotionmuseum/user/config/RedisConfig.class
com/emotionmuseum/user/service/impl/SliderCaptchaServiceImpl.class
com/emotionmuseum/user/vo/UserInfoResponse.class
com/emotionmuseum/user/config/CaptchaConfig.class
com/emotionmuseum/user/config/OAuthConfig.class
com/emotionmuseum/user/dto/UserUpdateRequest.class
com/emotionmuseum/user/dto/OAuthLoginRequest.class
com/emotionmuseum/user/dto/SliderCaptchaResponse.class
com/emotionmuseum/user/controller/OAuthController.class
com/emotionmuseum/user/UserApplication.class
com/emotionmuseum/user/service/impl/OAuthServiceImpl.class
com/emotionmuseum/user/dto/SliderCaptchaVerifyRequest.class
com/emotionmuseum/user/service/impl/SliderCaptchaServiceImpl$SliderCaptchaData.class
com/emotionmuseum/user/controller/UserController.class
com/emotionmuseum/user/config/AuthenticationConfig.class
com/emotionmuseum/user/service/UserService.class
com/emotionmuseum/user/service/impl/CaptchaServiceImpl.class
com/emotionmuseum/user/security/JwtAuthenticationFilter.class
com/emotionmuseum/user/entity/User.class
com/emotionmuseum/user/service/impl/UserServiceImpl.class
com/emotionmuseum/user/security/UserDetailsServiceImpl.class
com/emotionmuseum/user/dto/CaptchaResponse.class
com/emotionmuseum/user/service/CaptchaService.class
com/emotionmuseum/user/security/UserDetailsServiceImpl$SecurityUser.class
com/emotionmuseum/user/dto/RegisterRequest.class
com/emotionmuseum/user/config/CaptchaConfig.class
com/emotionmuseum/user/service/OAuthService.class
com/emotionmuseum/user/controller/CaptchaController.class
com/emotionmuseum/user/dto/LoginRequest.class
com/emotionmuseum/user/controller/UserController.class
com/emotionmuseum/user/security/JwtAuthenticationFilter.class
com/emotionmuseum/user/service/SliderCaptchaService.class
com/emotionmuseum/user/vo/UserInfoResponse$GrowthStatsVO.class
com/emotionmuseum/user/mapper/UserMapper.class
@@ -1,29 +1,15 @@
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/SecurityConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/vo/LoginResponse.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/RegisterRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/impl/CaptchaServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/security/JwtAuthenticationFilter.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/CaptchaResponse.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/impl/UserServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/controller/UserController.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/CaptchaConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/OAuthConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/UserService.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/OAuthService.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/SliderCaptchaVerifyRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/SliderCaptchaService.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/impl/OAuthServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/impl/SliderCaptchaServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/RedisConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/vo/UserInfoResponse.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/UserUpdateRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/entity/User.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/mapper/UserMapper.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/AuthenticationConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/security/UserDetailsServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/UserApplication.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/LoginRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/controller/OAuthController.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/OAuthLoginRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/impl/UserServiceImpl.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/service/CaptchaService.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/controller/UserController.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/SliderCaptchaResponse.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/OAuthConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/config/RedisConfig.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/dto/UserUpdateRequest.java
/Users/huazhongmin/peanut/AppleDevelop/EmotionMuseum/backend/emotion-user/src/main/java/com/emotionmuseum/user/controller/CaptchaController.java