diff --git a/.DS_Store b/.DS_Store index 7f5d6117ad..a47eeb6928 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md index 79fe1f3ad5..c2e3c9f6c7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:43:44 * @LastEditors: jack ning github@bytedesk.com - * @LastEditTime: 2024-04-26 14:11:29 + * @LastEditTime: 2024-04-30 09:34:00 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -42,9 +42,6 @@ AI powered customer service & team collaboration ```bash git clone https://github.com/Bytedesk/bytedesk.git -# open bytedesk/starter/src/main/resources/application-dev.properties -# change the value of spring.datasource.url and spring.datasource.username and spring.datasource.password -# change the value of spring.redis.host and spring.redis.port cd bytedesk/starter mvn spring-boot:run # diff --git a/README.zh.md b/README.zh.md index 53bd34eb5e..9439e9d450 100644 --- a/README.zh.md +++ b/README.zh.md @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:43:44 * @LastEditors: jack ning github@bytedesk.com - * @LastEditTime: 2024-04-26 14:11:35 + * @LastEditTime: 2024-04-30 09:26:12 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -13,9 +13,9 @@ * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. --> -# 微语 - 企业即时通讯 & 在线客服系统 & 大模型AI助手 +# 微语 -面向对数据安全比较敏感的中大型企业和组织,提供基于AI的企业即时通讯和在线客服系统 +基于AI的企业即时通讯和在线客服系统 ## 语言 diff --git a/h2db/weiyuim.mv.db b/h2db/weiyuim.mv.db new file mode 100644 index 0000000000..12e392c97a Binary files /dev/null and b/h2db/weiyuim.mv.db differ diff --git a/h2db/weiyuim.trace.db b/h2db/weiyuim.trace.db new file mode 100644 index 0000000000..fe84973316 --- /dev/null +++ b/h2db/weiyuim.trace.db @@ -0,0 +1,4098 @@ +2024-04-30 09:36:01.384720+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_CALENDARS" already exists; SQL statement: +CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR (200) NOT NULL , CALENDAR IMAGE NOT NULL ) [42101-224] +2024-04-30 09:36:01.385716+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_CRON_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , CRON_EXPRESSION VARCHAR (120) NOT NULL , TIME_ZONE_ID VARCHAR (80) ) [42101-224] +2024-04-30 09:36:01.386004+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_FIRED_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR (95) NOT NULL , TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , INSTANCE_NAME VARCHAR (200) NOT NULL , FIRED_TIME BIGINT NOT NULL , SCHED_TIME BIGINT NOT NULL , PRIORITY INTEGER NOT NULL , STATE VARCHAR (16) NOT NULL, JOB_NAME VARCHAR (200) NULL , JOB_GROUP VARCHAR (200) NULL , IS_NONCONCURRENT BOOLEAN NULL , REQUESTS_RECOVERY BOOLEAN NULL ) [42101-224] +2024-04-30 09:36:01.386175+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_PAUSED_TRIGGER_GRPS" already exists; SQL statement: +CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR (200) NOT NULL ) [42101-224] +2024-04-30 09:36:01.386333+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SCHEDULER_STATE" already exists; SQL statement: +CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR (200) NOT NULL , LAST_CHECKIN_TIME BIGINT NOT NULL , CHECKIN_INTERVAL BIGINT NOT NULL ) [42101-224] +2024-04-30 09:36:01.386539+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_LOCKS" already exists; SQL statement: +CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR (40) NOT NULL ) [42101-224] +2024-04-30 09:36:01.386757+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_JOB_DETAILS" already exists; SQL statement: +CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , JOB_CLASS_NAME VARCHAR (250) NOT NULL , IS_DURABLE BOOLEAN NOT NULL , IS_NONCONCURRENT BOOLEAN NOT NULL , IS_UPDATE_DATA BOOLEAN NOT NULL , REQUESTS_RECOVERY BOOLEAN NOT NULL , JOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:01.386928+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SIMPLE_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , REPEAT_COUNT BIGINT NOT NULL , REPEAT_INTERVAL BIGINT NOT NULL , TIMES_TRIGGERED BIGINT NOT NULL ) [42101-224] +2024-04-30 09:36:01.387125+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SIMPROP_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INTEGER NULL, INT_PROP_2 INTEGER NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 BOOLEAN NULL, BOOL_PROP_2 BOOLEAN NULL ) [42101-224] +2024-04-30 09:36:01.387293+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_BLOB_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , BLOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:01.387523+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , NEXT_FIRE_TIME BIGINT NULL , PREV_FIRE_TIME BIGINT NULL , PRIORITY INTEGER NULL , TRIGGER_STATE VARCHAR (16) NOT NULL , TRIGGER_TYPE VARCHAR (8) NOT NULL , START_TIME BIGINT NOT NULL , END_TIME BIGINT NULL , CALENDAR_NAME VARCHAR (200) NULL , MISFIRE_INSTR SMALLINT NULL , JOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:01.387735+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_CALENDARS" already exists; SQL statement: +ALTER TABLE QRTZ_CALENDARS ADD CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY ( SCHED_NAME, CALENDAR_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.389311+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_CRON_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.390664+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_FIRED_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_FIRED_TRIGGERS ADD CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY ( SCHED_NAME, ENTRY_ID ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.392340+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_PAUSED_TRIGGER_GRPS" already exists; SQL statement: +ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY ( SCHED_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.393472+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SCHEDULER_STATE" already exists; SQL statement: +ALTER TABLE QRTZ_SCHEDULER_STATE ADD CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY ( SCHED_NAME, INSTANCE_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.394804+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_LOCKS" already exists; SQL statement: +ALTER TABLE QRTZ_LOCKS ADD CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY ( SCHED_NAME, LOCK_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.395927+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_JOB_DETAILS" already exists; SQL statement: +ALTER TABLE QRTZ_JOB_DETAILS ADD CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.396974+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SIMPLE_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.397894+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SIMPROP_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.398719+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.399632+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.400547+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.402504+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:01.403482+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS" already exists; SQL statement: +ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) REFERENCES QRTZ_JOB_DETAILS ( SCHED_NAME, JOB_NAME, JOB_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$0() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.191145+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_CALENDARS" already exists; SQL statement: +CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR (200) NOT NULL , CALENDAR IMAGE NOT NULL ) [42101-224] +2024-04-30 09:36:44.192067+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_CRON_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , CRON_EXPRESSION VARCHAR (120) NOT NULL , TIME_ZONE_ID VARCHAR (80) ) [42101-224] +2024-04-30 09:36:44.192283+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_FIRED_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR (95) NOT NULL , TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , INSTANCE_NAME VARCHAR (200) NOT NULL , FIRED_TIME BIGINT NOT NULL , SCHED_TIME BIGINT NOT NULL , PRIORITY INTEGER NOT NULL , STATE VARCHAR (16) NOT NULL, JOB_NAME VARCHAR (200) NULL , JOB_GROUP VARCHAR (200) NULL , IS_NONCONCURRENT BOOLEAN NULL , REQUESTS_RECOVERY BOOLEAN NULL ) [42101-224] +2024-04-30 09:36:44.192429+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_PAUSED_TRIGGER_GRPS" already exists; SQL statement: +CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR (200) NOT NULL ) [42101-224] +2024-04-30 09:36:44.192603+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SCHEDULER_STATE" already exists; SQL statement: +CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR (200) NOT NULL , LAST_CHECKIN_TIME BIGINT NOT NULL , CHECKIN_INTERVAL BIGINT NOT NULL ) [42101-224] +2024-04-30 09:36:44.192742+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_LOCKS" already exists; SQL statement: +CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR (40) NOT NULL ) [42101-224] +2024-04-30 09:36:44.193062+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_JOB_DETAILS" already exists; SQL statement: +CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , JOB_CLASS_NAME VARCHAR (250) NOT NULL , IS_DURABLE BOOLEAN NOT NULL , IS_NONCONCURRENT BOOLEAN NOT NULL , IS_UPDATE_DATA BOOLEAN NOT NULL , REQUESTS_RECOVERY BOOLEAN NOT NULL , JOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:44.193291+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SIMPLE_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , REPEAT_COUNT BIGINT NOT NULL , REPEAT_INTERVAL BIGINT NOT NULL , TIMES_TRIGGERED BIGINT NOT NULL ) [42101-224] +2024-04-30 09:36:44.193499+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_SIMPROP_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INTEGER NULL, INT_PROP_2 INTEGER NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 BOOLEAN NULL, BOOL_PROP_2 BOOLEAN NULL ) [42101-224] +2024-04-30 09:36:44.193660+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_BLOB_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , BLOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:44.193885+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "QRTZ_TRIGGERS" already exists; SQL statement: +CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , NEXT_FIRE_TIME BIGINT NULL , PREV_FIRE_TIME BIGINT NULL , PRIORITY INTEGER NULL , TRIGGER_STATE VARCHAR (16) NOT NULL , TRIGGER_TYPE VARCHAR (8) NOT NULL , START_TIME BIGINT NOT NULL , END_TIME BIGINT NULL , CALENDAR_NAME VARCHAR (200) NULL , MISFIRE_INSTR SMALLINT NULL , JOB_DATA IMAGE NULL ) [42101-224] +2024-04-30 09:36:44.194112+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_CALENDARS" already exists; SQL statement: +ALTER TABLE QRTZ_CALENDARS ADD CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY ( SCHED_NAME, CALENDAR_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.195810+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_CRON_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.197198+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_FIRED_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_FIRED_TRIGGERS ADD CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY ( SCHED_NAME, ENTRY_ID ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.198669+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_PAUSED_TRIGGER_GRPS" already exists; SQL statement: +ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY ( SCHED_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.199771+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SCHEDULER_STATE" already exists; SQL statement: +ALTER TABLE QRTZ_SCHEDULER_STATE ADD CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY ( SCHED_NAME, INSTANCE_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.200827+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_LOCKS" already exists; SQL statement: +ALTER TABLE QRTZ_LOCKS ADD CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY ( SCHED_NAME, LOCK_NAME ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.201884+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_JOB_DETAILS" already exists; SQL statement: +ALTER TABLE QRTZ_JOB_DETAILS ADD CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.202928+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SIMPLE_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.204597+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_SIMPROP_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.205731+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "PK_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.206971+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.207859+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.208681+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS" already exists; SQL statement: +ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) +2024-04-30 09:36:44.209494+08:00 jdbc[3]: exception +org.h2.jdbc.JdbcSQLSyntaxErrorException: Constraint "FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS" already exists; SQL statement: +ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) REFERENCES QRTZ_JOB_DETAILS ( SCHED_NAME, JOB_NAME, JOB_GROUP ) [90045-224] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:644) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + at org.h2.message.DbException.get(DbException.java:223) + at org.h2.message.DbException.get(DbException.java:199) + at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:111) + at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:74) + at org.h2.command.ddl.AlterTable.update(AlterTable.java:46) + at org.h2.command.CommandContainer.update(CommandContainer.java:169) + at org.h2.command.Command.executeUpdate(Command.java:256) + at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262) + at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) + at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254) + at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54) + at org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer.runScripts(DataSourceScriptDatabaseInitializer.java:87) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.runScripts(AbstractScriptDatabaseInitializer.java:146) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applyScripts(AbstractScriptDatabaseInitializer.java:108) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.applySchemaScripts(AbstractScriptDatabaseInitializer.java:98) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.initializeDatabase(AbstractScriptDatabaseInitializer.java:76) + at org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer.afterPropertiesSet(AbstractScriptDatabaseInitializer.java:66) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:312) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:546) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1173) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.lambda$createEndpointBean$1(EndpointDiscoverer.java:145) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean.getBean(EndpointDiscoverer.java:456) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getFilterEndpoint(EndpointDiscoverer.java:314) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isFilterMatch(EndpointDiscoverer.java:290) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.isExtensionExposed(EndpointDiscoverer.java:244) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBean(EndpointDiscoverer.java:170) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.addExtensionBeans(EndpointDiscoverer.java:159) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:124) + at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration.getHealthEndpoint(HealthEndpointWebExtensionConfiguration.java:80) + at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.healthEndpointWebMvcHandlerMapping(HealthEndpointWebExtensionConfiguration.java:95) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:663) + at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1327) + at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:368) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.initHandlerMappings(HandlerMappingIntrospector.java:130) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.afterPropertiesSet(HandlerMappingIntrospector.java:118) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1179) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:116) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:207) + at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:360) + at com.bytedesk.starter.config.SecurityConfig.lambda$securityFilterChain$3(SecurityConfig.java:62) + at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeHttpRequests(HttpSecurity.java:1467) + at com.bytedesk.starter.config.SecurityConfig.securityFilterChain(SecurityConfig.java:61) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$3() + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) + at com.bytedesk.starter.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:643) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1166) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1687) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1651) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1541) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1509) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:872) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:833) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) + at com.bytedesk.starter.StarterApplication.main(StarterApplication.java:30) diff --git a/modules/.DS_Store b/modules/.DS_Store index 1d954d5a01..1b187718cb 100644 Binary files a/modules/.DS_Store and b/modules/.DS_Store differ diff --git a/modules/ai/.DS_Store b/modules/ai/.DS_Store index c05305064f..c6286d2b2b 100644 Binary files a/modules/ai/.DS_Store and b/modules/ai/.DS_Store differ diff --git a/modules/ai/pom.xml b/modules/ai/pom.xml index 5d8ec56abf..5abc319d70 100644 --- a/modules/ai/pom.xml +++ b/modules/ai/pom.xml @@ -7,11 +7,10 @@ com.bytedesk modules - 0.0.1-SNAPSHOT - weiyu-ai + im-ai ai Demo project for Spring Boot @@ -59,7 +58,7 @@ com.bytedesk - weiyu-core + im-core ${im.version} provided diff --git a/modules/ai/src/main/java/com/bytedesk/ai/kb/Kb.java b/modules/ai/src/main/java/com/bytedesk/ai/kb/Kb.java index ef54781f70..985286c86f 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/kb/Kb.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/kb/Kb.java @@ -56,7 +56,7 @@ public class Kb extends AuditModel { private String embeddings; // is_published or not - // private Boolean published; + // private boolean published; /** * 所属用户 diff --git a/modules/ai/src/main/java/com/bytedesk/ai/kb/KbService.java b/modules/ai/src/main/java/com/bytedesk/ai/kb/KbService.java index a63fc5fcb9..cacd155134 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/kb/KbService.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/kb/KbService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-22 16:46:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-26 16:52:17 + * @LastEditTime: 2024-04-11 12:08:49 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -20,8 +20,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; import lombok.AllArgsConstructor; @@ -33,6 +33,8 @@ public class KbService { private final ModelMapper modelMapper; + private final UidUtils uidUtils; + public Page query(KbRequest kbRequest) { Pageable pageable = PageRequest.of(kbRequest.getPageNumber(), kbRequest.getPageSize(), Sort.Direction.DESC, @@ -44,7 +46,7 @@ public class KbService { public JsonResult create(KbRequest kbRequest) { Kb kb = modelMapper.map(kbRequest, Kb.class); - kb.setKid(Utils.getUid()); + kb.setKid(uidUtils.getCacheSerialUid()); // kb.setUser(authService.getCurrentUser()); @@ -54,11 +56,10 @@ public class KbService { } - @SuppressWarnings("null") public Kb getKb(String name) { Kb kb = Kb.builder() - .kid(Utils.getUid()) + .kid(uidUtils.getCacheSerialUid()) .name(name) .vectorStore("redis") .embeddings("m3e-base") diff --git a/modules/ai/src/main/java/com/bytedesk/ai/llm/LlmService.java b/modules/ai/src/main/java/com/bytedesk/ai/llm/LlmService.java index b8d367871d..a2cae1a8d6 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/llm/LlmService.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/llm/LlmService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-25 12:08:16 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-26 16:03:31 + * @LastEditTime: 2024-04-11 12:09:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,8 +15,8 @@ package com.bytedesk.ai.llm; import org.springframework.stereotype.Service; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; import lombok.AllArgsConstructor; @@ -26,7 +26,7 @@ public class LlmService { private LlmRepository llmRepository; - + private final UidUtils uidUtils; public JsonResult create(LlmRequest llmRequest) { @@ -36,7 +36,7 @@ public class LlmService { public Llm getLlm(String type) { Llm llm = new Llm(); - llm.setLid(Utils.getUid()); + llm.setLid(uidUtils.getCacheSerialUid()); llm.setName("智谱AI"); llm.setDescription("对接智谱API"); llm.setType(type); diff --git a/modules/ai/src/main/java/com/bytedesk/ai/robot/Robot.java b/modules/ai/src/main/java/com/bytedesk/ai/robot/Robot.java index d5917c2a15..a1d77f9761 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/robot/Robot.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/robot/Robot.java @@ -74,7 +74,7 @@ public class Robot extends AuditModel { private String type; // is_published or not - private Boolean published; + private boolean published; /** * llm diff --git a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotRequest.java b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotRequest.java index 807af1b16c..ff5346d415 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotRequest.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-22 16:45:07 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 16:42:02 + * @LastEditTime: 2024-04-16 12:01:55 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -35,7 +35,7 @@ public class RobotRequest extends BaseRequest { private String welcome; - private Boolean published; + // private boolean published; // diff --git a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotResponse.java b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotResponse.java index 6c95e0a86b..18801d8170 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotResponse.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotResponse.java @@ -35,5 +35,5 @@ public class RobotResponse { private String welcome; - private Boolean published; + private boolean published; } diff --git a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotService.java b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotService.java index d48adb7e24..7a33bce20d 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotService.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/robot/RobotService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-22 16:44:41 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 17:14:46 + * @LastEditTime: 2024-04-17 23:57:32 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -24,12 +24,12 @@ import org.springframework.stereotype.Service; import com.bytedesk.ai.kb.KbService; import com.bytedesk.ai.llm.LlmService; -import com.bytedesk.core.auth.AuthService; import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; import lombok.AllArgsConstructor; @@ -49,6 +49,8 @@ public class RobotService { private final ModelMapper modelMapper; + private final UidUtils uidUtils; + public Page query(RobotRequest robotRequest) { User user = authService.getCurrentUser(); @@ -71,7 +73,7 @@ public class RobotService { // Robot robot = modelMapper.map(robotRequest, Robot.class); // - String rid = Utils.getUid(); + String rid = uidUtils.getCacheSerialUid(); robot.setRid(rid); robot.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL); @@ -90,7 +92,7 @@ public class RobotService { } - @SuppressWarnings("null") + // @SuppressWarnings("null") public void initData() { if (robotRepository.count() > 0) { @@ -101,7 +103,7 @@ public class RobotService { Optional adminOptional = userService.getAdmin(); if (adminOptional.isPresent()) { // - String rid = Utils.getUid(); + String rid = uidUtils.getCacheSerialUid(); Robot robot = Robot.builder() .rid(rid) .name("客服机器人") diff --git a/modules/blog/.DS_Store b/modules/blog/.DS_Store new file mode 100644 index 0000000000..c8e40f1f7e Binary files /dev/null and b/modules/blog/.DS_Store differ diff --git a/modules/oa/.gitignore b/modules/blog/.gitignore similarity index 100% rename from modules/oa/.gitignore rename to modules/blog/.gitignore diff --git a/modules/oa/.mvn/wrapper/maven-wrapper.jar b/modules/blog/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from modules/oa/.mvn/wrapper/maven-wrapper.jar rename to modules/blog/.mvn/wrapper/maven-wrapper.jar diff --git a/modules/oa/.mvn/wrapper/maven-wrapper.properties b/modules/blog/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from modules/oa/.mvn/wrapper/maven-wrapper.properties rename to modules/blog/.mvn/wrapper/maven-wrapper.properties diff --git a/modules/oa/build.gradle b/modules/blog/build.gradle similarity index 62% rename from modules/oa/build.gradle rename to modules/blog/build.gradle index 1f9a278769..3bddbfe813 100644 --- a/modules/oa/build.gradle +++ b/modules/blog/build.gradle @@ -21,7 +21,16 @@ dependencies { // compileOnly 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.springframework.boot:spring-boot-starter-thymeleaf' - + // + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // implementation 'com.mysql:mysql-connector-j' + // + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-rest + // https://spring.io/guides/tutorials/react-and-spring-data-rest/ + implementation 'org.springframework.boot:spring-boot-starter-data-rest' // // for api docs // https://springdoc.org/ diff --git a/modules/oa/gradle/wrapper/gradle-wrapper.jar b/modules/blog/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from modules/oa/gradle/wrapper/gradle-wrapper.jar rename to modules/blog/gradle/wrapper/gradle-wrapper.jar diff --git a/modules/oa/gradle/wrapper/gradle-wrapper.properties b/modules/blog/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from modules/oa/gradle/wrapper/gradle-wrapper.properties rename to modules/blog/gradle/wrapper/gradle-wrapper.properties diff --git a/modules/oa/gradlew b/modules/blog/gradlew similarity index 100% rename from modules/oa/gradlew rename to modules/blog/gradlew diff --git a/modules/oa/gradlew.bat b/modules/blog/gradlew.bat similarity index 100% rename from modules/oa/gradlew.bat rename to modules/blog/gradlew.bat diff --git a/modules/oa/mvnw b/modules/blog/mvnw similarity index 100% rename from modules/oa/mvnw rename to modules/blog/mvnw diff --git a/modules/oa/mvnw.cmd b/modules/blog/mvnw.cmd similarity index 100% rename from modules/oa/mvnw.cmd rename to modules/blog/mvnw.cmd diff --git a/modules/oa/pom.xml b/modules/blog/pom.xml similarity index 61% rename from modules/oa/pom.xml rename to modules/blog/pom.xml index f9c679465f..7a5edbf842 100644 --- a/modules/oa/pom.xml +++ b/modules/blog/pom.xml @@ -11,29 +11,17 @@ 0.0.1-SNAPSHOT - - weiyu-oa - + im-blog - oa + blog Demo project for Spring Boot - + + + - - - - - com.bytedesk - weiyu-core - ${im.version} - provided - - diff --git a/modules/oa/settings.gradle b/modules/blog/settings.gradle similarity index 71% rename from modules/oa/settings.gradle rename to modules/blog/settings.gradle index 6ef3a2607a..d0b56e297b 100644 --- a/modules/oa/settings.gradle +++ b/modules/blog/settings.gradle @@ -1,3 +1,3 @@ -rootProject.name = 'oa' +rootProject.name = 'blog' include ':core' project(':core').projectDir = new File('../core') \ No newline at end of file diff --git a/modules/oa/src/main/java/com/bytedesk/oa/OaApplication.java b/modules/blog/src/main/java/com/bytedesk/blog/BlogApplication.java similarity index 63% rename from modules/oa/src/main/java/com/bytedesk/oa/OaApplication.java rename to modules/blog/src/main/java/com/bytedesk/blog/BlogApplication.java index 4497c9a1e1..005183f96e 100644 --- a/modules/oa/src/main/java/com/bytedesk/oa/OaApplication.java +++ b/modules/blog/src/main/java/com/bytedesk/blog/BlogApplication.java @@ -1,13 +1,13 @@ -package com.bytedesk.oa; +package com.bytedesk.blog; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class OaApplication { +public class BlogApplication { public static void main(String[] args) { - SpringApplication.run(OaApplication.class, args); + SpringApplication.run(BlogApplication.class, args); } } diff --git a/modules/blog/src/main/resources/application.properties b/modules/blog/src/main/resources/application.properties new file mode 100644 index 0000000000..4d636cbf1a --- /dev/null +++ b/modules/blog/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=blog diff --git a/modules/oa/src/test/java/com/bytedesk/oa/OaApplicationTests.java b/modules/blog/src/test/java/com/bytedesk/blog/BlogApplicationTests.java similarity index 72% rename from modules/oa/src/test/java/com/bytedesk/oa/OaApplicationTests.java rename to modules/blog/src/test/java/com/bytedesk/blog/BlogApplicationTests.java index 926998573d..5d0c65ed41 100644 --- a/modules/oa/src/test/java/com/bytedesk/oa/OaApplicationTests.java +++ b/modules/blog/src/test/java/com/bytedesk/blog/BlogApplicationTests.java @@ -1,10 +1,10 @@ -package com.bytedesk.oa; +package com.bytedesk.blog; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class OaApplicationTests { +class BlogApplicationTests { @Test void contextLoads() { diff --git a/modules/core/.DS_Store b/modules/core/.DS_Store index 6bffcdbabe..4a0beaf22b 100644 Binary files a/modules/core/.DS_Store and b/modules/core/.DS_Store differ diff --git a/modules/core/build.gradle b/modules/core/build.gradle index 81bc04655d..fb93ef18e1 100644 --- a/modules/core/build.gradle +++ b/modules/core/build.gradle @@ -51,10 +51,6 @@ dependencies { implementation 'com.alibaba.fastjson2:fastjson2:2.0.45' // https://mvnrepository.com/artifact/org.modelmapper/modelmapper implementation 'org.modelmapper:modelmapper:3.2.0' - // docs: https://doc.hutool.cn/pages/index/#%F0%9F%93%9A%E7%AE%80%E4%BB%8B - // https://mvnrepository.com/artifact/cn.hutool/hutool-all - implementation 'cn.hutool:hutool-all:5.8.25' - // for api docs // https://springdoc.org/ diff --git a/modules/core/pom.xml b/modules/core/pom.xml index fc88616864..d572d8ebb1 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -11,13 +11,13 @@ 0.0.1-SNAPSHOT - weiyu-core - jar + im-core core Demo project for Spring Boot + 0.12.5 @@ -53,14 +53,14 @@ io.jsonwebtoken jjwt-api - 0.12.3 + ${jsonwebtoken.version} io.jsonwebtoken jjwt-impl - 0.12.3 + ${jsonwebtoken.version} runtime @@ -68,17 +68,10 @@ io.jsonwebtoken jjwt-jackson - 0.12.3 + ${jsonwebtoken.version} runtime - - - cn.hutool - hutool-all - 5.8.25 - - diff --git a/modules/core/src/main/java/com/bytedesk/core/.DS_Store b/modules/core/src/main/java/com/bytedesk/core/.DS_Store index 39a56b49ce..4c93fb161b 100644 Binary files a/modules/core/src/main/java/com/bytedesk/core/.DS_Store and b/modules/core/src/main/java/com/bytedesk/core/.DS_Store differ diff --git a/modules/core/src/main/java/com/bytedesk/core/action/Action.java b/modules/core/src/main/java/com/bytedesk/core/action/Action.java new file mode 100644 index 0000000000..a3c3ac7305 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/action/Action.java @@ -0,0 +1,58 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:31:38 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:36:07 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.action; + +import com.bytedesk.core.utils.AuditModel; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * + */ +@Data +@Entity +@Builder +@EqualsAndHashCode(callSuper=false) +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "core_action") +public class Action extends AuditModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(unique = true, nullable = false) + private String aid; + + private String title; + + private String action; + + private String description; + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/action/ActionController.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionController.java new file mode 100644 index 0000000000..ff0865addd --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionController.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:40:19 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:40:21 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.action; + +public class ActionController { + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/action/ActionLogAspect.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionLogAspect.java new file mode 100644 index 0000000000..8772273868 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionLogAspect.java @@ -0,0 +1,89 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-17 16:53:12 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 23:41:33 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.action; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import com.bytedesk.core.annotation.ActionLog; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 操作日志记录处理 + * + * @author jackning + */ +@Slf4j +@Async +@Aspect +@Component +@AllArgsConstructor +public class ActionLogAspect { + + private final ActionService actionService; + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, ActionLog controllerLog) { + log.debug("actionLog before: model {}, action {}", controllerLog.title(), controllerLog.action()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, ActionLog controllerLog, Object jsonResult) { + log.debug("actionLog after returning: model {}, action {}, jsonResult {}", controllerLog.title(), + controllerLog.action(), jsonResult); + // handleLog(joinPoint, controllerLog, null, jsonResult); + // + ActionRequest actionRequest = ActionRequest.builder() + .title(controllerLog.title()) + .action(controllerLog.action()) + .description(controllerLog.description()) + .build(); + actionService.create(actionRequest); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, ActionLog controllerLog, Exception e) { + log.info("actionLog after throwing: model {}, action {}", controllerLog.title(), controllerLog.action()); + // handleLog(joinPoint, controllerLog, e, null); + } + + + // protected void handleLog(final JoinPoint joinPoint, ActionLog controllerLog, final Exception e, Object jsonResult) { + // log.debug("actionLog handleLog: model {}, action {}", controllerLog.title(), controllerLog.action()); + // // TODO: write to db + // } + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/cache/CaffeineConfig.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionRepository.java similarity index 75% rename from modules/core/src/main/java/com/bytedesk/core/cache/CaffeineConfig.java rename to modules/core/src/main/java/com/bytedesk/core/action/ActionRepository.java index b01a24299a..ebe1a9db34 100644 --- a/modules/core/src/main/java/com/bytedesk/core/cache/CaffeineConfig.java +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionRepository.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-28 13:00:51 + * @Date: 2024-04-25 15:40:53 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-28 13:15:15 + * @LastEditTime: 2024-04-25 15:40:55 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,11 +12,11 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.cache; +package com.bytedesk.core.action; -import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ActionRepository extends JpaRepository { -@Configuration -public class CaffeineConfig { -} +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/action/ActionRequest.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionRequest.java new file mode 100644 index 0000000000..83bc1c7d68 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionRequest.java @@ -0,0 +1,42 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:40:29 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 23:41:25 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.action; + +import com.bytedesk.core.utils.BaseRequest; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +public class ActionRequest extends BaseRequest { + + private static final long serialVersionUID = 2108168382L; + + private String aid; + + private String title; + + private String action; + + private String description; + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionResponse.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionResponse.java similarity index 69% rename from modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionResponse.java rename to modules/core/src/main/java/com/bytedesk/core/action/ActionResponse.java index 7192492ef9..df05728bfe 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionResponse.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:24 + * @Date: 2024-04-25 15:40:39 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:03:33 + * @LastEditTime: 2024-04-25 15:46:33 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,26 +12,28 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.rbac.action; +package com.bytedesk.core.action; import com.bytedesk.core.utils.BaseResponse; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; -/** - * 对应权限Authority中操作:query/create/update/delete/import/export - * - * @author bytedesk.com on 2019-08-05 - */ @Data -@EqualsAndHashCode(callSuper = true) +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) public class ActionResponse extends BaseResponse { - + + private static final long serialVersionUID = -4636716962L; + private String aid; + + private String title; - private String name; - - private String value; + private String action; + private String description; } diff --git a/modules/core/src/main/java/com/bytedesk/core/action/ActionService.java b/modules/core/src/main/java/com/bytedesk/core/action/ActionService.java new file mode 100644 index 0000000000..8249129ceb --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/action/ActionService.java @@ -0,0 +1,50 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:41:47 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:52:51 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.action; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.uid.UidUtils; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class ActionService { + + private final ActionRepository actionRepository; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + public Action create(ActionRequest actionRequest) { + + Action action = modelMapper.map(actionRequest, Action.class); + action.setAid(uidUtils.getCacheSerialUid()); + + return save(action); + } + + public Action save(Action action) { + return actionRepository.save(action); + } + + public ActionResponse convertToActionResponse(Action action) { + return modelMapper.map(action, ActionResponse.class); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/annotation/ActionLog.java b/modules/core/src/main/java/com/bytedesk/core/annotation/ActionLog.java new file mode 100644 index 0000000000..1f60741f8f --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/annotation/ActionLog.java @@ -0,0 +1,39 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-17 16:53:05 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:38:58 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义操作日志记录注解 + * + * @author jackning + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ActionLog { + + public String title() default ""; + + public String action() default ""; + + public String description() default ""; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/annotation/ApiRateLimiter.java b/modules/core/src/main/java/com/bytedesk/core/annotation/ApiRateLimiter.java new file mode 100644 index 0000000000..e3df9852e9 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/annotation/ApiRateLimiter.java @@ -0,0 +1,56 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-09 11:58:06 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 11:58:12 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +import org.springframework.core.annotation.AliasFor; + +/** + * https://blog.csdn.net/MICHAELKING1/article/details/106058874 + * + * @author dyz + * @version 1.0 + * @date 2020/5/11 15:40 api rate limiter + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(value = RetentionPolicy.RUNTIME) +@Documented +public @interface ApiRateLimiter { + + // + int NOT_LIMITED = 0; + + /** + * qps + */ + @AliasFor("qps") double value() default NOT_LIMITED; + + /** + * qps + */ + @AliasFor("value") double qps() default NOT_LIMITED; + + /** + * 超时时长 + */ + int timeout() default 0; + + /** + * 超时时间单位 + */ + TimeUnit timeUnit() default TimeUnit.SECONDS; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/aop/ApiRateLimiterAspect.java b/modules/core/src/main/java/com/bytedesk/core/aop/ApiRateLimiterAspect.java new file mode 100644 index 0000000000..c4a32cf73f --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/aop/ApiRateLimiterAspect.java @@ -0,0 +1,80 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-09 11:59:04 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 12:49:23 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.aop; + +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.annotation.ApiRateLimiter; +import com.bytedesk.core.utils.JsonResult; + +import lombok.extern.slf4j.Slf4j; +import com.google.common.util.concurrent.RateLimiter; + + +/** + * https://blog.csdn.net/MICHAELKING1/article/details/106058874 + */ +@Slf4j +@Aspect +@Component +public class ApiRateLimiterAspect { + + private static final ConcurrentMap RATE_LIMITER_CACHE = new ConcurrentHashMap<>(); + + @Pointcut("@annotation(com.bytedesk.core.annotation.ApiRateLimiter)") + public void apiRateLimit() {} + + @Around("apiRateLimit()") + public Object pointcut(ProceedingJoinPoint point) throws Throwable { + // + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + // 通过 AnnotationUtils.findAnnotation 获取 RateLimiter 注解 + ApiRateLimiter apiRateLimiter = AnnotationUtils.findAnnotation(method, ApiRateLimiter.class); + // + boolean proceed = true; + if (apiRateLimiter != null && apiRateLimiter.qps() > ApiRateLimiter.NOT_LIMITED) { + double qps = apiRateLimiter.qps(); + if (RATE_LIMITER_CACHE.get(method.getName()) == null) { + // 初始化 QPS + RATE_LIMITER_CACHE.put(method.getName(), RateLimiter.create(qps)); + } + log.debug("api {}, {}", method.getName(), RATE_LIMITER_CACHE.get(method.getName()).getRate()); + // 尝试获取令牌 + if (RATE_LIMITER_CACHE.get(method.getName()) != null && !RATE_LIMITER_CACHE.get(method.getName()) + .tryAcquire(apiRateLimiter.timeout(), apiRateLimiter.timeUnit())) { + proceed = false; + log.debug("api out of limit, please try later"); + } + } + // + if (proceed) { + return point.proceed(); + } + // + return JsonResult.error("api out of limit, please try later"); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/Asistant.java b/modules/core/src/main/java/com/bytedesk/core/asistant/Asistant.java new file mode 100644 index 0000000000..6aa5755ade --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/Asistant.java @@ -0,0 +1,67 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 20:32:23 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 10:55:02 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import com.bytedesk.core.utils.AuditModel; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * asistant - 如:文件助手 + */ +@Entity +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({ AsistantListener.class }) +@Table(name = "core_asistant") +public class Asistant extends AuditModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(unique = true, nullable = false, length = 127) + private String aid; + + private String topic; + + @Column(name = "by_type") + private String type; + + private String name; + + private String avatar; + + private String description; + + /** belong to org */ + private String orgOid; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantController.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantController.java new file mode 100644 index 0000000000..07b690eae3 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantController.java @@ -0,0 +1,58 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:04:43 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 22:54:06 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.bytedesk.core.utils.JsonResult; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +/** + * + * http://localhost:9003/swagger-ui/index.html + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/asistant") +@Tag(name = "asistant - 助手", description = "asistant apis") +public class AsistantController { + + private final AsistantService asistantService; + + /** + * query asistant + * + * @return json + */ + @GetMapping("/query") + public ResponseEntity query(AsistantRequest asistantRequest) { + // + Page asistantPage = asistantService.query(asistantRequest); + // + return ResponseEntity.ok(JsonResult.success(asistantPage)); + } + + + + + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantListener.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantListener.java new file mode 100644 index 0000000000..4b3701b958 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantListener.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-27 12:09:59 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 13:00:43 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class AsistantListener { + + + @PostPersist + public void onPostPersist(Asistant asistant) { + log.debug("AsistantListener: onPostPersist {}", asistant.getName()); + + + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRepository.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRepository.java new file mode 100644 index 0000000000..075defdb0b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRepository.java @@ -0,0 +1,21 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:07:55 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 21:10:46 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AsistantRepository extends JpaRepository { + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRequest.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRequest.java new file mode 100644 index 0000000000..a69ae9ea51 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantRequest.java @@ -0,0 +1,42 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:05:09 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 12:51:21 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import com.bytedesk.core.utils.BaseRequest; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class AsistantRequest extends BaseRequest { + + private String aid; + + private String topic; + + private String name; + + private String avatar; + + private String description; + + /** belong to org */ + private String orgOid; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantResponse.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantResponse.java new file mode 100644 index 0000000000..fc153d8dc7 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantResponse.java @@ -0,0 +1,44 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:05:21 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 10:55:31 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import com.bytedesk.core.utils.BaseResponse; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class AsistantResponse extends BaseResponse { + + private String aid; + + private String topic; + + private String type; + + private String name; + + private String avatar; + + private String description; + + /** belong to org */ + private String orgOid; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantService.java b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantService.java new file mode 100644 index 0000000000..16bf06908c --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/asistant/AsistantService.java @@ -0,0 +1,135 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:04:54 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 11:14:48 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.asistant; + +import java.util.Optional; + +import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.constant.I18Consts; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; +import com.bytedesk.core.constant.TopicConsts; +import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.rbac.user.User; +import com.bytedesk.core.rbac.user.UserRequest; +import com.bytedesk.core.rbac.user.UserResponseSimple; +import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.thread.ThreadService; +import com.bytedesk.core.thread.Thread; +import com.bytedesk.core.uid.UidUtils; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class AsistantService { + + private final AsistantRepository asistantRepository; + + private final UserService userService; + + private final ThreadService threadService; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + public Page query(AsistantRequest asistantRequest) { + + Pageable pageable = PageRequest.of(asistantRequest.getPageNumber(), asistantRequest.getPageSize(), Sort.Direction.ASC, + "id"); + + Page asistantPage = asistantRepository.findAll(pageable); + + return asistantPage.map(asistant -> convertToAsistantResponse(asistant)); + } + + public Asistant create(AsistantRequest asistantRequest) { + + Asistant asistant = modelMapper.map(asistantRequest, Asistant.class); + asistant.setAid(uidUtils.getCacheSerialUid()); + + return save(asistant); + } + + private Asistant save(Asistant asistant) { + return asistantRepository.save(asistant); + } + + public AsistantResponse convertToAsistantResponse(Asistant asistant) { + return modelMapper.map(asistant, AsistantResponse.class); + } + + + // + public void initData() { + + if (asistantRepository.count() > 0) { + return; + } + + Optional adminOptional = userService.getAdmin(); + + AsistantRequest asistantRequest = AsistantRequest.builder() + .topic(TopicConsts.TOPIC_FILE_ASISTANT) + .name(I18Consts.I18_FILE_ASISTANT_NAME) + .avatar(AvatarConsts.DEFAULT_FILE_ASISTANT_AVATAR_URL) + .description(I18Consts.I18_FILE_ASISTANT_DESCRIPTION) + .orgOid(adminOptional.get().getOrgOid()) + .build(); + asistantRequest.setType(TypeConsts.TYPE_SYSTEM); + create(asistantRequest); + + // 方便测试,默认给每个初始用户生成一个跟 文件助手 的对话 + UserRequest userRequest = new UserRequest(); + userRequest.setPageNumber(0); + userRequest.setPageSize(10); + // + Page userPage = userService.query(userRequest); + userPage.forEach(user -> { + // + UserResponseSimple userSimple = UserResponseSimple.builder() + .uid(asistantRequest.getAid()) + .nickname(asistantRequest.getName()) + .avatar(asistantRequest.getAvatar()) + .build(); + // + Thread thread = Thread.builder() + .tid(uidUtils.getCacheSerialUid()) + .type(ThreadTypeConsts.ASISTANT) + .topic(TopicConsts.TOPIC_FILE_ASISTANT + "/" + user.getUid()) + .status(StatusConsts.THREAD_STATUS_INIT) + .client(TypeConsts.TYPE_SYSTEM) + .user(JSON.toJSONString(userSimple)) + .owner(user) + .orgOid(asistantRequest.getOrgOid()) + .build(); + + threadService.save(thread); + }); + + } + + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthAspect.java b/modules/core/src/main/java/com/bytedesk/core/auth/AuthAspect.java deleted file mode 100644 index 7d6aaa3287..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthAspect.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-02-26 11:45:58 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 12:02:57 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.auth; - -import org.aspectj.lang.annotation.After; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; -import org.springframework.stereotype.Component; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Aspect -@Component -public class AuthAspect { - - @Pointcut("execution(* com.bytedesk.core.auth.AuthController.*(..))") - public void authLog() { - log.debug("ActionAspect authLog"); - }; - - @Before("authLog()") - public void beforeAuthLog() { - log.debug("ActionAspect beforeAuthLog"); - } - - @After("authLog()") - public void afterAuthLog() { - log.debug("ActionAspect afterAuthLog"); - // TODO: action log save to db - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/block/Block.java b/modules/core/src/main/java/com/bytedesk/core/black/Black.java similarity index 89% rename from modules/core/src/main/java/com/bytedesk/core/block/Block.java rename to modules/core/src/main/java/com/bytedesk/core/black/Black.java index 8af769e15b..e437ed46f1 100644 --- a/modules/core/src/main/java/com/bytedesk/core/block/Block.java +++ b/modules/core/src/main/java/com/bytedesk/core/black/Black.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-28 22:02:34 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-28 22:02:36 + * @LastEditTime: 2024-04-13 15:24:14 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,11 +12,11 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.block; +package com.bytedesk.core.black; /** * 黑名单 */ -public class Block { +public class Black { } diff --git a/modules/core/src/main/java/com/bytedesk/core/cache/CacheConfig.java b/modules/core/src/main/java/com/bytedesk/core/cache/CacheConfig.java index 19953d934b..99109d602f 100644 --- a/modules/core/src/main/java/com/bytedesk/core/cache/CacheConfig.java +++ b/modules/core/src/main/java/com/bytedesk/core/cache/CacheConfig.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-27 18:45:02 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 00:04:11 + * @LastEditTime: 2024-04-15 15:23:56 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,102 +14,17 @@ */ package com.bytedesk.core.cache; -// import java.time.Duration; -import java.util.Arrays; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.caffeine.CaffeineCacheManager; -import org.springframework.cache.support.CompositeCacheManager; -// import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair; /** * cache config + * https://docs.spring.io/spring-framework/reference/integration/cache/annotations.html#cache-spel-context * https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#io.caching * https://www.51cto.com/article/753777.html */ @Configuration @EnableCaching public class CacheConfig { - - // @Autowired - // private BytedeskProperties bytedeskProperties; - - @Autowired - private RedisConnectionFactory redisConnectionFactory; - - // @Value("${cache.caffeine.maximumSize}") - // private int maximumSize; - - // @Value("${cache.caffeine.expireAfterWriteSeconds}") - // private int expireAfterWriteSeconds; - - // @Value("${spring.cache.redis.time-to-live}") - // private long redisTimeToLiveSeconds; - - // @Bean - // public CacheManager cacheManager() { - // if (bytedeskProperties.getCacheLevel() == 0) { - // return new NoOpCacheManager(); - // } else if (bytedeskProperties.getCacheLevel() == 1) { - // return caffeineCacheManager(); - // } else { - // return compositeCacheManager(caffeineCacheManager(), redisCacheManager()); - // } - // } - - // @SuppressWarnings("null") - public CaffeineCacheManager caffeineCacheManager() { - CaffeineCacheManager cacheManager = new CaffeineCacheManager(); - // cacheManager.setCaffeine(caffeineCacheBuilder()); - return cacheManager; - } - - // private Caffeine caffeineCacheBuilder() { - // return Caffeine.newBuilder() - // .expireAfterWrite(expireAfterWriteSeconds, TimeUnit.SECONDS) - // .maximumSize(maximumSize); - // } - - /** - * https://docs.spring.io/spring-data/redis/reference/redis/redis-cache.html - * 使用自定义rediscache时,遇到下面错误: - * FIXME: SerializationException: Could not read JSON:failed to lazily initialize a collection: could not initialize proxy - no Session - * @return - */ - // @Bean - // @SuppressWarnings("null") - public RedisCacheManager redisCacheManager() { - return RedisCacheManager.builder(redisConnectionFactory) - .cacheDefaults(cacheConfiguration()) - .build(); - } - - public RedisCacheConfiguration cacheConfiguration() { - return RedisCacheConfiguration.defaultCacheConfig() - .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); - } - - /** - * FIXME: 二级缓存未触发redis缓存? - * @param caffeineCacheManager - * @param redisCacheManager - * @return - */ - // @SuppressWarnings("null") - public CompositeCacheManager compositeCacheManager(CaffeineCacheManager caffeineCacheManager, - RedisCacheManager redisCacheManager) { - CompositeCacheManager compositeCacheManager = new CompositeCacheManager(); - // Arrays.asList(caffeineCacheManager, redisCacheManager) 中caffeineCacheManager放在前面, - // 即先查询caffeineCacheManager缓存,未命中则查询redisCacheManager。顺序很重要,别搞错 - compositeCacheManager.setCacheManagers(Arrays.asList(caffeineCacheManager, redisCacheManager)); - compositeCacheManager.setFallbackToNoOpCache(false); // 关闭缓存未命中时自动创建的空缓存 - return compositeCacheManager; - } } diff --git a/modules/core/src/main/java/com/bytedesk/core/cache/RedisConfig.java b/modules/core/src/main/java/com/bytedesk/core/cache/RedisConfig.java deleted file mode 100644 index 46917055b6..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/cache/RedisConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-03-28 13:00:34 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 09:30:38 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.cache; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -// import org.springframework.data.redis.serializer.GenericToStringSerializer; -// import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; -// import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -/** - * https://docs.spring.io/spring-data/redis/reference/redis/template.html - */ -@Configuration -public class RedisConfig { - - @Autowired - private RedisConnectionFactory redisConnectionFactory; - - @Bean - StringRedisTemplate stringRedisTemplate() { - StringRedisTemplate template = new StringRedisTemplate(); - template.setConnectionFactory(redisConnectionFactory); - return template; - } - - // @Bean - // RedisTemplate redisTemplate() { - // RedisTemplate template = new RedisTemplate<>(); - // template.setConnectionFactory(redisConnectionFactory); - // return template; - // } - - @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashValueSerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); - redisTemplate.setConnectionFactory(redisConnectionFactory); - return redisTemplate; - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/Action.java b/modules/core/src/main/java/com/bytedesk/core/channel/Channel.java similarity index 62% rename from modules/core/src/main/java/com/bytedesk/core/rbac/action/Action.java rename to modules/core/src/main/java/com/bytedesk/core/channel/Channel.java index 99086f5621..2f115a7c94 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/Action.java +++ b/modules/core/src/main/java/com/bytedesk/core/channel/Channel.java @@ -1,60 +1,69 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:24 + * @Date: 2024-04-26 20:34:52 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:02:22 + * @LastEditTime: 2024-04-28 11:20:53 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE * contact: 270580156@qq.com * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.rbac.action; +package com.bytedesk.core.channel; import com.bytedesk.core.utils.AuditModel; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** - * 对应权限Authority中操作:query/create/update/delete/export/import - * - * @author bytedesk.com on 2019-08-05 + * channel 频道 - 类似公众号 */ +@Entity @Data +@Builder @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = "core_action") -public class Action extends AuditModel { - - private static final long serialVersionUID = -4364459013162121569L; +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({ChannelListener.class}) +@Table(name = "core_channel") +public class Channel extends AuditModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(unique = true, nullable = false, length = 127) - private String aid; + private String cid; + private String topic; + + @Column(name = "by_type") + private String type; + private String name; - - private String value; + + private String avatar; private String description; - // @JsonIgnore - // @ManyToMany(mappedBy = "actions", fetch = FetchType.LAZY) - // private Set authorities = new HashSet<>(); + /** belong to org */ + private String orgOid; + } diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelController.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelController.java new file mode 100644 index 0000000000..7c0dea7bfd --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelController.java @@ -0,0 +1,55 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:06:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 23:01:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.bytedesk.core.utils.JsonResult; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +/** + * + * http://localhost:9003/swagger-ui/index.html + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/channel") +@Tag(name = "channel - 频道", description = "channel apis") +public class ChannelController { + + private final ChannelService channelService; + + /** + * query channel + * + * @return json + */ + @GetMapping("/query") + public ResponseEntity query(ChannelRequest channelRequest) { + // + Page channelPage = channelService.query(channelRequest); + // + return ResponseEntity.ok(JsonResult.success(channelPage)); + } + + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelListener.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelListener.java new file mode 100644 index 0000000000..aba37d19fe --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelListener.java @@ -0,0 +1,31 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-28 11:19:41 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 11:19:44 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class ChannelListener { + + @PostPersist + public void onPostPersist(Channel channel) { + log.info("onPostPersist: {}", channel.getName()); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRepository.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRepository.java new file mode 100644 index 0000000000..0841600ad0 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRepository.java @@ -0,0 +1,21 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:07:38 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 21:10:57 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChannelRepository extends JpaRepository { + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRequest.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRequest.java new file mode 100644 index 0000000000..b666cb192b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelRequest.java @@ -0,0 +1,42 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:07:10 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 21:44:27 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import com.bytedesk.core.utils.BaseRequest; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class ChannelRequest extends BaseRequest { + + private String cid; + + private String topic; + + private String name; + + private String avatar; + + private String description; + + /** belong to org */ + private String orgOid; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelResponse.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelResponse.java new file mode 100644 index 0000000000..170de66301 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelResponse.java @@ -0,0 +1,44 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:07:22 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 10:55:42 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import com.bytedesk.core.utils.BaseResponse; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class ChannelResponse extends BaseResponse { + + private String cid; + + private String topic; + + private String type; + + private String name; + + private String avatar; + + private String description; + + /** belong to org */ + private String orgOid; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/channel/ChannelService.java b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelService.java new file mode 100644 index 0000000000..7e1232dafb --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/channel/ChannelService.java @@ -0,0 +1,97 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:06:12 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 11:40:10 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.channel; + +import java.util.Optional; + +import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.constant.I18Consts; +import com.bytedesk.core.constant.TopicConsts; +import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.rbac.user.User; +import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.uid.UidUtils; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class ChannelService { + + private final ChannelRepository channelRepository; + + private final UserService userService; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + public Page query(ChannelRequest channelRequest) { + + Pageable pageable = PageRequest.of(channelRequest.getPageNumber(), channelRequest.getPageSize(), Sort.Direction.ASC, + "id"); + + Page channelPage = channelRepository.findAll(pageable); + + return channelPage.map(channel -> convertToChannelResponse(channel)); + } + + public Channel create(ChannelRequest channelRequest) { + + Channel channel = modelMapper.map(channelRequest, Channel.class); + channel.setCid(uidUtils.getCacheSerialUid()); + + return save(channel); + } + + private Channel save(Channel channel) { + return channelRepository.save(channel); + } + + public ChannelResponse convertToChannelResponse(Channel channel) { + return modelMapper.map(channel, ChannelResponse.class); + } + + public void initData() { + + if (channelRepository.count() > 0) { + return; + } + + Optional adminOptional = userService.getAdmin(); + + ChannelRequest channelRequest = ChannelRequest.builder() + .topic(TopicConsts.TOPIC_SYSTEM_NOTIFICATION) + .name(I18Consts.I18_SYSTEM_NOTIFICATION_NAME) + .avatar(AvatarConsts.DEFAULT_SYSTEM_NOTIFICATION_AVATAR_URL) + .description(I18Consts.I18_SYSTEM_NOTIFICATION_DESCRIPTION) + .orgOid(adminOptional.get().getOrgOid()) + .build(); + channelRequest.setType(TypeConsts.TYPE_SYSTEM); + create(channelRequest); + // + + + } + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/config/BytedeskConfig.java b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskConfig.java index b51b2ea3a3..e23fc17699 100644 --- a/modules/core/src/main/java/com/bytedesk/core/config/BytedeskConfig.java +++ b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskConfig.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-30 07:52:26 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 14:08:54 + * @LastEditTime: 2024-04-22 23:32:51 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -26,6 +26,8 @@ import org.springframework.security.config.annotation.authentication.configurati import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.client.RestTemplate; +import com.bytedesk.core.utils.ApplicationContextHolder; + import lombok.Getter; /** @@ -58,4 +60,9 @@ public class BytedeskConfig { return restTemplate; } + @Bean + public ApplicationContextHolder applicationContextHolder() { + return new ApplicationContextHolder(); + } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/config/BytedeskProperties.java b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskProperties.java index 81a1a02a13..f35f9f4596 100644 --- a/modules/core/src/main/java/com/bytedesk/core/config/BytedeskProperties.java +++ b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskProperties.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-30 09:14:39 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-28 10:55:27 + * @LastEditTime: 2024-04-24 10:18:37 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -30,13 +30,15 @@ public class BytedeskProperties { private String password; + private String nickname; + private String email; private String mobile; private String company; - private String timezone; + // private String timezone; // cors private String corsAllowedOrigins; diff --git a/modules/core/src/main/java/com/bytedesk/core/push/IosService.java b/modules/core/src/main/java/com/bytedesk/core/connect/Connect.java similarity index 85% rename from modules/core/src/main/java/com/bytedesk/core/push/IosService.java rename to modules/core/src/main/java/com/bytedesk/core/connect/Connect.java index d8c2c5c75d..94a239c1b2 100644 --- a/modules/core/src/main/java/com/bytedesk/core/push/IosService.java +++ b/modules/core/src/main/java/com/bytedesk/core/connect/Connect.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-31 15:30:08 + * @Date: 2024-04-15 16:58:27 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-31 15:30:11 + * @LastEditTime: 2024-04-15 16:58:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,8 +12,8 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.push; +package com.bytedesk.core.connect; -public class IosService { +public class Connect { } diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/AvatarConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/AvatarConsts.java index 67bc2cc43d..d80981c864 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/AvatarConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/AvatarConsts.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 16:39:55 + * @LastEditTime: 2024-04-28 11:10:14 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,7 +15,8 @@ package com.bytedesk.core.constant; /** - * 头像常量 + * 头像常量, 可以去这里获取 https://www.iconfont.cn/ + * TODO: 头像不能引用外部链接,使用本地或本服务器地址 * * @author bytedesk.com */ @@ -134,6 +135,12 @@ public class AvatarConsts { */ public static final String DEFAULT_GROUP_AVATAR_URL = "https://bytedesk.oss-cn-shenzhen.aliyuncs.com/avatars/group_default_avatar.png"; + // 文件助手头像 + public static final String DEFAULT_FILE_ASISTANT_AVATAR_URL = "https://chainsnow.oss-cn-shenzhen.aliyuncs.com/avatars/file_asistant_avatar.png"; + + // 系统通知-公众号头像 + public static final String DEFAULT_SYSTEM_NOTIFICATION_AVATAR_URL = "https://chainsnow.oss-cn-shenzhen.aliyuncs.com/avatars/notification.png"; + /** * 测试头像: * https://bytedesk.oss-cn-shenzhen.aliyuncs.com/avatars/girl.png diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/BdConstants.java b/modules/core/src/main/java/com/bytedesk/core/constant/BdConstants.java index 83c57a029e..49196d1a99 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/BdConstants.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/BdConstants.java @@ -19,7 +19,8 @@ public class BdConstants { public static final boolean IS_DEBUG = false; // 空字符串 - public static final String EMPTY = ""; + public static final String EMPTY_STRING = ""; + public static final String EMPTY_JSON_STRING = "{}"; /** * Path separator. diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/I18Consts.java b/modules/core/src/main/java/com/bytedesk/core/constant/I18Consts.java new file mode 100644 index 0000000000..5d7792b3cf --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/constant/I18Consts.java @@ -0,0 +1,37 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 22:25:47 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-28 11:11:03 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.constant; + +// 国际化常量 +public class I18Consts { + + private I18Consts() { + } + + public static final String EN = "en"; + public static final String ZH = "zh"; + // public static final String EN_US = "en-us"; + // public static final String ZH_CN = "zh-cn"; + + // "文件助手" + public static final String I18_FILE_ASISTANT_NAME = "file_asistant"; + // "手机、电脑文件互传" + public static final String I18_FILE_ASISTANT_DESCRIPTION = "file_asistant_description"; + // 系统通知 + public static final String I18_SYSTEM_NOTIFICATION_NAME = "system_notification"; + public static final String I18_SYSTEM_NOTIFICATION_DESCRIPTION = "system_notification_description"; + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/MessageTypeConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/MessageTypeConsts.java index 626932099b..aa07153c05 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/MessageTypeConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/MessageTypeConsts.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-22 15:51:39 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:59:49 + * @LastEditTime: 2024-04-11 17:20:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -581,4 +581,15 @@ public class MessageTypeConsts { public static final String NOTIFICATION_GROUP_DISMISS = "notification_group_dismiss"; + + /** + * 回执 + */ + // 服务器回复客户端,已经收到。消息发送成功 + public static final String NOTIFICATION_ACK_SUCCESS = "notification_ack_success"; + // 消息送达 + public static final String NOTIFICATION_ACK_RECEIVED = "notification_ack_received"; + // 消息已读 + public static final String NOTIFICATION_ACK_READ = "notification_ack_read"; + } diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/RedisConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/RedisConsts.java index 45a00ff1fd..e0eedaca1a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/RedisConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/RedisConsts.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-27 10:55:25 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 13:02:38 + * @LastEditTime: 2024-04-16 12:39:23 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -285,5 +285,6 @@ public class RedisConsts { // redis pub/sub public static final String REDIS_PUBSUB_CHANNEL_TOPIC = "redispubsubchannel:message"; public static final String REDIS_PUBSUB_CHANNEL_TOPIC_OBJECT = "redispubsubchannel:messageobject"; + public static final String REDIS_PUBSUB_CHANNEL_TOPIC_RESPONSE = "redispubsubchannel:messageresponse"; } diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/StatusConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/StatusConsts.java index 5db5c57d61..74909585f0 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/StatusConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/StatusConsts.java @@ -237,13 +237,16 @@ public class StatusConsts { public static final String ANSWER_STATUS_PUBLISHED = "published"; public static final String ANSWER_STATUS_UNPUBLISHED = "unpublished"; - /** - * 学校认领:待审核、审核通过、审核拒绝 - */ - public static final String CLAIM_STATUS_WAITING = "waiting"; - public static final String CLAIM_STATUS_ACCEPT = "accept"; - public static final String CLAIM_STATUS_REJECT = "reject"; - + // 同事和群组会话初始值为init + public static final String THREAD_STATUS_INIT = "init"; + // 只有客服会话会有open + public static final String THREAD_STATUS_OPEN = "open"; + public static final String THREAD_STATUS_CLOSED_AUTO = "closed_auto"; + public static final String THREAD_STATUS_CLOSED_AGENT = "closed_agent"; + // 发送验证码状态 + public static final String CODE_STATUS_PENDING = "pending"; + public static final String CODE_STATUS_CONFIRM = "confirmed"; + public static final String CODE_STATUS_EXPIRED = "expired"; } diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/ThreadTypeConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/ThreadTypeConsts.java index 192f7e550a..1b76322279 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/ThreadTypeConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/ThreadTypeConsts.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-22 16:01:14 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 16:01:17 + * @LastEditTime: 2024-04-27 12:37:31 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,17 +15,20 @@ package com.bytedesk.core.constant; public class ThreadTypeConsts { + private ThreadTypeConsts() { } /** * thread type */ - public static final String WORKGROUP = "workgroup"; public static final String APPOINTED = "appointed"; + public static final String WORKGROUP = "workgroup"; public static final String MEMBER = "member"; public static final String GROUP = "group"; public static final String ROBOT = "robot"; public static final String LEAVEMSG = "leavemsg"; public static final String FEEDBACK = "feedback"; + public static final String ASISTANT = "assistant"; + public static final String CHANNEL = "channel"; } diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/TopicConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/TopicConsts.java new file mode 100644 index 0000000000..f4cd5229c0 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/constant/TopicConsts.java @@ -0,0 +1,26 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 21:51:31 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 21:51:34 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.constant; + +public class TopicConsts { + + private TopicConsts() { + } + + // public static final String TOPIC_PREFIX = "topic"; + public static final String TOPIC_FILE_ASISTANT = "fileAssistant"; + public static final String TOPIC_SYSTEM_NOTIFICATION = "systemNotification"; + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/constant/TypeConsts.java b/modules/core/src/main/java/com/bytedesk/core/constant/TypeConsts.java index c2c286eace..919c6fb10d 100644 --- a/modules/core/src/main/java/com/bytedesk/core/constant/TypeConsts.java +++ b/modules/core/src/main/java/com/bytedesk/core/constant/TypeConsts.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 16:02:05 + * @LastEditTime: 2024-04-26 09:41:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -20,31 +20,47 @@ public class TypeConsts { } public static final String TYPE_SYSTEM = "system"; + public static final String TYPE_MOBILE = "mobile"; + public static final String TYPE_EMAIL = "email"; // ROLES - 角色 + public static final String ROLE_ = "ROLE_"; + + // super - 超级管理员 + public static final String ROLE_SUPER = "ROLE_SUPER"; + public static final String AUTHORITY_SUPER = "SUPER"; + // admin - 管理员 public static final String ROLE_ADMIN = "ROLE_ADMIN"; + public static final String AUTHORITY_ADMIN = "ADMIN"; // hr - 人事 public static final String ROLE_HR = "ROLE_HR"; + public static final String AUTHORITY_HR = "HR"; // org - 行政 public static final String ROLE_ORG = "ROLE_ORG"; + public static final String AUTHORITY_ORG = "ORG"; // it - IT public static final String ROLE_IT = "ROLE_IT"; + public static final String AUTHORITY_IT = "IT"; // money - 财务 public static final String ROLE_MONEY = "ROLE_MONEY"; + public static final String AUTHORITY_MONEY = "MONEY"; // marketing - 市场 public static final String ROLE_MARKETING = "ROLE_MARKETING"; + public static final String AUTHORITY_MARKETING = "MARKETING"; // sales - 销售 public static final String ROLE_SALES = "ROLE_SALES"; + public static final String AUTHORITY_SALES = "SALES"; // customer service - 客服 public static final String ROLE_CUSTOMER_SERVICE = "ROLE_CS"; + public static final String AUTHORITY_CUSTOMER_SERVICE = "CS"; /// 部门 // hr - 人事 @@ -69,6 +85,11 @@ public class TypeConsts { public static final String DEPT_CUSTOMER_SERVICE = "DEPT_CS"; + // + public static final String SEND_MOBILE_CODE_TYPE_LOGIN = "login"; + public static final String SEND_MOBILE_CODE_TYPE_REGISTER = "register"; + public static final String SEND_MOBILE_CODE_TYPE_FORGET = "forget"; + public static final String SEND_MOBILE_CODE_TYPE_VERIFY = "verify"; diff --git a/modules/core/src/main/java/com/bytedesk/core/event/BytedeskEventPublisher.java b/modules/core/src/main/java/com/bytedesk/core/event/BytedeskEventPublisher.java index 24adebb2ca..c9486f2a83 100644 --- a/modules/core/src/main/java/com/bytedesk/core/event/BytedeskEventPublisher.java +++ b/modules/core/src/main/java/com/bytedesk/core/event/BytedeskEventPublisher.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-23 14:42:58 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 14:38:43 + * @LastEditTime: 2024-04-23 08:55:55 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,8 +15,10 @@ package com.bytedesk.core.event; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; +import com.bytedesk.core.thread.Thread; import lombok.AllArgsConstructor; @Component @@ -25,16 +27,50 @@ public class BytedeskEventPublisher { private final ApplicationEventPublisher applicationEventPublisher; + @Async public void publishMessageBytesEvent(byte[] messageBytes) { applicationEventPublisher.publishEvent(new MessageBytesEvent(this, messageBytes)); } + @Async public void publishMessageJsonEvent(String json) { applicationEventPublisher.publishEvent(new MessageJsonEvent(this, json)); } + @Async public void publishQuartzFiveSecondEvent() { applicationEventPublisher.publishEvent(new QuartzFiveSecondEvent(this)); } - + + @Async + public void publishMqttConnectedEvent(String client) { + applicationEventPublisher.publishEvent(new MqttConnectedEvent(this, client)); + } + + @Async + public void publishMqttDisconnectedEvent(String client) { + applicationEventPublisher.publishEvent(new MqttDisconnectedEvent(this, client)); + } + + // @Async + // public void publishMqttSubscribeEvent(String uid, String topic) { + // applicationEventPublisher.publishEvent(new MqttSubscribeEvent(this, uid, topic)); + // } + + // @Async + // public void publishMqttUnsubscribeEvent(String uid, String topic) { + // applicationEventPublisher.publishEvent(new MqttUnsubscribeEvent(this, uid, topic)); + // } + + @Async + public void publishThreadCreateEvent(Thread thread) { + applicationEventPublisher.publishEvent(new ThreadCreateEvent(this, thread)); + } + + @Async + public void publishThreadUpdateEvent(Thread thread) { + applicationEventPublisher.publishEvent(new ThreadUpdateEvent(this, thread)); + } + + } diff --git a/modules/core/src/main/java/com/bytedesk/core/event/MessageJsonEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/MessageJsonEvent.java index 172e463c37..d57656184c 100644 --- a/modules/core/src/main/java/com/bytedesk/core/event/MessageJsonEvent.java +++ b/modules/core/src/main/java/com/bytedesk/core/event/MessageJsonEvent.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-02-23 14:43:57 + * @Date: 2024-02-28 11:43:29 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 11:43:44 + * @LastEditTime: 2024-04-12 18:02:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPubRelMessage.java b/modules/core/src/main/java/com/bytedesk/core/event/MqttConnectedEvent.java old mode 100755 new mode 100644 similarity index 63% rename from modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPubRelMessage.java rename to modules/core/src/main/java/com/bytedesk/core/event/MqttConnectedEvent.java index 17312c586e..38be286a0e --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPubRelMessage.java +++ b/modules/core/src/main/java/com/bytedesk/core/event/MqttConnectedEvent.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 + * @Date: 2024-02-23 14:43:57 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:42:59 + * @LastEditTime: 2024-04-15 14:21:09 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,25 +12,22 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.socket.mqtt.model; +package com.bytedesk.core.event; -import java.io.Serializable; +import org.springframework.context.ApplicationEvent; import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; +import lombok.EqualsAndHashCode; -/** - * PUBREL重发消息存储 - */ @Data -@Accessors(chain = true) -@NoArgsConstructor -public class MqttDupPubRelMessage implements Serializable { - - private static final long serialVersionUID = -4111642532532950980L; - +@EqualsAndHashCode(callSuper = false) +public class MqttConnectedEvent extends ApplicationEvent { + private String clientId; - private int messageId; + public MqttConnectedEvent(Object source, String clientId) { + super(source); + this.clientId = clientId; + } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/event/MqttDisconnectedEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/MqttDisconnectedEvent.java new file mode 100644 index 0000000000..4975ca36f5 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/event/MqttDisconnectedEvent.java @@ -0,0 +1,33 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-02-23 14:43:57 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 14:21:25 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.event; + +import org.springframework.context.ApplicationEvent; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class MqttDisconnectedEvent extends ApplicationEvent { + + private String clientId; + + public MqttDisconnectedEvent(Object source, String clientId) { + super(source); + this.clientId = clientId; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/event/MqttSubscribeEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/MqttSubscribeEvent.java new file mode 100644 index 0000000000..9ca01db61a --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/event/MqttSubscribeEvent.java @@ -0,0 +1,35 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-02-23 14:43:57 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 10:16:12 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.event; + +// import org.springframework.context.ApplicationEvent; + +// import lombok.Data; +// import lombok.EqualsAndHashCode; + +// @Data +// @EqualsAndHashCode(callSuper = false) +// public class MqttSubscribeEvent extends ApplicationEvent { + +// private String uid; +// private String topic; + +// public MqttSubscribeEvent(Object source, String uid, String topic) { +// super(source); +// this.uid = uid; +// this.topic = topic; +// } + +// } diff --git a/modules/core/src/main/java/com/bytedesk/core/event/MqttUnsubscribeEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/MqttUnsubscribeEvent.java new file mode 100644 index 0000000000..ffc9a166a0 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/event/MqttUnsubscribeEvent.java @@ -0,0 +1,35 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-02-23 14:43:57 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 10:16:17 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.event; + +// import org.springframework.context.ApplicationEvent; + +// import lombok.Data; +// import lombok.EqualsAndHashCode; + +// @Data +// @EqualsAndHashCode(callSuper = false) +// public class MqttUnsubscribeEvent extends ApplicationEvent { + +// private String uid; +// private String topic; + +// public MqttUnsubscribeEvent(Object source, String uid, String topic) { +// super(source); +// this.uid = uid; +// this.topic = topic; +// } + +// } diff --git a/modules/core/src/main/java/com/bytedesk/core/event/ThreadCreateEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/ThreadCreateEvent.java new file mode 100644 index 0000000000..08ed532d70 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/event/ThreadCreateEvent.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-23 08:51:27 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 08:54:21 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.event; + +import com.bytedesk.core.thread.Thread; +import org.springframework.context.ApplicationEvent; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ThreadCreateEvent extends ApplicationEvent { + + private Thread thread; + + public ThreadCreateEvent(Object source, Thread thread) { + super(source); + this.thread = thread; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/event/ThreadUpdateEvent.java b/modules/core/src/main/java/com/bytedesk/core/event/ThreadUpdateEvent.java new file mode 100644 index 0000000000..7b69e573fe --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/event/ThreadUpdateEvent.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-23 08:51:27 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 08:54:08 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.event; + +import com.bytedesk.core.thread.Thread; +import org.springframework.context.ApplicationEvent; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ThreadUpdateEvent extends ApplicationEvent { + + private Thread thread; + + public ThreadUpdateEvent(Object source, Thread thread) { + super(source); + this.thread = thread; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/BaseException.java b/modules/core/src/main/java/com/bytedesk/core/exception/BaseException.java new file mode 100644 index 0000000000..bd42010ae1 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/BaseException.java @@ -0,0 +1,26 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 09:29:11 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 09:59:23 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +public abstract class BaseException extends RuntimeException { + + public BaseException(String message) { + super(message); + } + + public BaseException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/EmailExistsException.java b/modules/core/src/main/java/com/bytedesk/core/exception/EmailExistsException.java new file mode 100644 index 0000000000..ec1d9920d7 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/EmailExistsException.java @@ -0,0 +1,28 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 09:28:30 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 09:58:48 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +public class EmailExistsException extends BaseException { + + private static final long serialVersionUID = 5543722167L; + + public EmailExistsException(String message) { + super(message); + //TODO Auto-generated constructor stub + } + + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/EmailNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/exception/EmailNotFoundException.java similarity index 92% rename from modules/core/src/main/java/com/bytedesk/core/utils/exception/EmailNotFoundException.java rename to modules/core/src/main/java/com/bytedesk/core/exception/EmailNotFoundException.java index 9269591066..9883801bfb 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/EmailNotFoundException.java +++ b/modules/core/src/main/java/com/bytedesk/core/exception/EmailNotFoundException.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-03 10:58:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-03 10:59:43 + * @LastEditTime: 2024-04-26 11:26:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.utils.exception; +package com.bytedesk.core.exception; import org.springframework.security.core.AuthenticationException; diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/ForbiddenException.java b/modules/core/src/main/java/com/bytedesk/core/exception/ForbiddenException.java new file mode 100755 index 0000000000..97cc3a5658 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/ForbiddenException.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:24 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 11:26:36 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +/** + * Exception caused by accessing forbidden resources. + * + * @author johnniang + */ +public class ForbiddenException extends BaseException { + + private static final long serialVersionUID = -6029126336570526306L; + + public ForbiddenException(String message) { + super(message); + } + + public ForbiddenException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/GlobalControllerAdvice.java b/modules/core/src/main/java/com/bytedesk/core/exception/GlobalControllerAdvice.java new file mode 100644 index 0000000000..e5f09add31 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/GlobalControllerAdvice.java @@ -0,0 +1,100 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 09:31:29 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 15:06:12 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.resource.NoResourceFoundException; + +import com.bytedesk.core.utils.JsonResult; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ControllerAdvice +public class GlobalControllerAdvice { + + @ExceptionHandler(EmailExistsException.class) + public ResponseEntity handleEmailExistsException(EmailExistsException e) { + return ResponseEntity.ok().body(JsonResult.error("Email already exists, please login or use another one")); + } + + @ExceptionHandler(MobileExistsException.class) + public ResponseEntity handleMobileExistsException(MobileExistsException e) { + return ResponseEntity.ok().body(JsonResult.error("Mobile already exists, please login or use another one")); + } + + @ExceptionHandler(UsernameNotFoundException.class) + public ResponseEntity handleUsernameNotFoundException(UsernameNotFoundException e) { + return ResponseEntity.ok().body(JsonResult.error("User not found, please signup first")); + } + + @ExceptionHandler(EmailNotFoundException.class) + public ResponseEntity handleEmailNotFoundException(EmailNotFoundException e) { + return ResponseEntity.ok().body(JsonResult.error("Email not found, please signup first")); + } + + @ExceptionHandler(MobileNotFoundException.class) + public ResponseEntity handleMobileNotFoundException(MobileNotFoundException e) { + return ResponseEntity.ok().body(JsonResult.error("Mobile not found, please signup first")); + } + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity handleNotFoundException(NotFoundException e) { + return ResponseEntity.ok().body(JsonResult.error("Resource not found, usually is uuids not exist")); + } + + @ExceptionHandler(UserDisabledException.class) + public ResponseEntity handleUserDisabledException(UserDisabledException e) { + return ResponseEntity.ok().body(JsonResult.error("User disabled, please contact admin")); + } + + @ExceptionHandler(ForbiddenException.class) + public ResponseEntity handleForbiddenException(ForbiddenException e) { + return ResponseEntity.ok().body(JsonResult.error("Forbidden to access this resource")); + } + + @ExceptionHandler(InternalAuthenticationServiceException.class) + public ResponseEntity handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) { + return ResponseEntity.ok().body(JsonResult.error("User blocked, please contact admin")); + } + + @ExceptionHandler(NoResourceFoundException.class) + public ResponseEntity handleNoResourceFoundException(NoResourceFoundException e) { + return ResponseEntity.ok().body(JsonResult.error( + "Api Resource not found, or It's a vip api, you should contact 270580156@qq.com or visit http://www.weiyuai.cn", + 404)); + } + + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity handleBadCredentialsException(BadCredentialsException e) { + return ResponseEntity.ok().body(JsonResult.error("Username or password is incorrect")); + } + + @ExceptionHandler(value = NullPointerException.class) + public ResponseEntity handleNullPointerException(NullPointerException ex) { + return ResponseEntity.badRequest().body(JsonResult.error("Null Pointer Exception")); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + log.error("not handled exception:", e); + return ResponseEntity.badRequest().body(JsonResult.error("Internal Server Error")); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/MobileExistsException.java b/modules/core/src/main/java/com/bytedesk/core/exception/MobileExistsException.java new file mode 100644 index 0000000000..b3ba445b2e --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/MobileExistsException.java @@ -0,0 +1,26 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 09:28:30 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 09:59:04 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +public class MobileExistsException extends BaseException { + + private static final long serialVersionUID = -873386350L; + + public MobileExistsException(String message) { + super(message); + //TODO Auto-generated constructor stub + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/MobileNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/exception/MobileNotFoundException.java similarity index 92% rename from modules/core/src/main/java/com/bytedesk/core/utils/exception/MobileNotFoundException.java rename to modules/core/src/main/java/com/bytedesk/core/exception/MobileNotFoundException.java index 69526a53d8..8163018f0e 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/MobileNotFoundException.java +++ b/modules/core/src/main/java/com/bytedesk/core/exception/MobileNotFoundException.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-03 10:58:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-03 10:59:04 + * @LastEditTime: 2024-04-26 11:28:23 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.utils.exception; +package com.bytedesk.core.exception; import org.springframework.security.core.AuthenticationException; diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/NotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/exception/NotFoundException.java new file mode 100644 index 0000000000..ed55e67961 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/NotFoundException.java @@ -0,0 +1,29 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-26 12:35:08 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:35:10 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +/** + * Resource not found + */ +public class NotFoundException extends BaseException { + + private static final long serialVersionUID = -984846602L; + + public NotFoundException(String message) { + super(message); + //TODO Auto-generated constructor stub + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/exception/UserDisabledException.java b/modules/core/src/main/java/com/bytedesk/core/exception/UserDisabledException.java new file mode 100755 index 0000000000..217c389f48 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/exception/UserDisabledException.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:24 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 11:33:41 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.exception; + +/** + * Exception caused by disabled user. + * + * @author jackning + */ +public class UserDisabledException extends BaseException { + + private static final long serialVersionUID = -3297016350L; + + public UserDisabledException(String message) { + super(message); + } + + public UserDisabledException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/ip/IpController.java b/modules/core/src/main/java/com/bytedesk/core/ip/IpController.java index 2505606fc0..50fe3627fc 100644 --- a/modules/core/src/main/java/com/bytedesk/core/ip/IpController.java +++ b/modules/core/src/main/java/com/bytedesk/core/ip/IpController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-05 14:15:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 14:37:36 + * @LastEditTime: 2024-04-08 00:08:19 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -51,6 +51,8 @@ public class IpController { /** * http://localhost:9003/ip/api/v1/location + * location: "国家|区域|省份|城市|ISP" + * location: "中国|0|湖北省|武汉市|联通" * * @param request * @return @@ -59,6 +61,7 @@ public class IpController { public JsonResult location(HttpServletRequest request) { String ip = IpUtils.clientIp(request); + // location: "中国|0|湖北省|武汉市|联通" String location = ipService.getIpLocation(ip); // JSONObject jsonObject = new JSONObject(); @@ -77,6 +80,8 @@ public class IpController { @GetMapping("/ip/location") public JsonResult ipLocation(@RequestParam String ip) { + // location: "国家|区域|省份|城市|ISP" + // location: "中国|0|湖北省|武汉市|联通" String location = ipService.getIpLocation(ip); // JSONObject jsonObject = new JSONObject(); diff --git a/modules/core/src/main/java/com/bytedesk/core/ip/IpService.java b/modules/core/src/main/java/com/bytedesk/core/ip/IpService.java index 1dd331bdbd..077cfbb38b 100644 --- a/modules/core/src/main/java/com/bytedesk/core/ip/IpService.java +++ b/modules/core/src/main/java/com/bytedesk/core/ip/IpService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-16 13:28:03 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 14:34:16 + * @LastEditTime: 2024-04-08 10:24:39 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,6 +17,7 @@ package com.bytedesk.core.ip; import org.lionsoul.ip2region.xdb.Searcher; import org.springframework.stereotype.Service; +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,6 +31,22 @@ public class IpService { private final Searcher searcher; + + /** + * 获取客户端ip + * @param request + * @return + */ + public String getIp(HttpServletRequest request) { + return IpUtils.clientIp(request); + } + + /** + * location: "国家|区域|省份|城市|ISP" + * location: "中国|0|湖北省|武汉市|联通" + * @param ip + * @return + */ public String getIpLocation(String ip) { try { return searcher.search(ip); @@ -39,4 +56,9 @@ public class IpService { return null; } + public String getIpLocation(HttpServletRequest request) { + String ip = getIp(request); + return getIpLocation(ip); + } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/message/Message.java b/modules/core/src/main/java/com/bytedesk/core/message/Message.java index 8fae05f535..7cf04e6534 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/Message.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/Message.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 16:12:05 + * @LastEditTime: 2024-04-22 22:21:00 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,7 +17,10 @@ package com.bytedesk.core.message; import java.util.ArrayList; import java.util.List; -import com.bytedesk.core.rbac.user.User; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.thread.Thread; import com.bytedesk.core.utils.AuditModel; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -40,67 +43,54 @@ import lombok.experimental.Accessors; @Table(name = "core_message") public class Message extends AuditModel { + private static final long serialVersionUID = 6816837318L; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - /** - * - */ @Column(unique = true, nullable = false) private String mid; - /** - * - * 消息类型 - */ @Column(name = "by_type") private String type; - /** - * - */ @Column(length = 512) private String content; - /** - * - */ - @Lob - private String extra; + // @Lob + // @Builder.Default + // @Column(columnDefinition = "json") + // @JdbcTypeCode(SqlTypes.JSON) + // private String extra = BdConstants.EMPTY_JSON_STRING; - /** - * - */ + /** send/stored/read */ private String status; - /** - * - */ private String client; - /** - * message belongs to - */ - // @JsonIgnore - // @ManyToOne(fetch = FetchType.LAZY) - // @JoinColumn(name = "thread_id", foreignKey = @ForeignKey(name = "none", value - // = ConstraintMode.NO_CONSTRAINT)) - // private Thread thread; - + /** message belongs to */ @JsonIgnore @Builder.Default @ManyToMany(fetch = FetchType.LAZY) - @JoinTable(name = "core_message_threads", - joinColumns = @JoinColumn(name = "message_id"), - inverseJoinColumns = @JoinColumn(name = "thread_id")) private List threads = new ArrayList<>(); /** * sender + * 考虑到访客信息不存储在user表中,在visitor表中,此处使用json存储,加快查询速度, + * 以空间换时间 */ - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) - private User user; + // @ManyToOne(fetch = FetchType.EAGER) + // private User user; + // + // h2 db 不能使用 user, 所以重定义为 by_user + @Builder.Default + @Column(name = "by_user", columnDefinition = "json") + @JdbcTypeCode(SqlTypes.JSON) + private String user = BdConstants.EMPTY_JSON_STRING; + + // TODO: + /** belong to org */ + private String orgOid; } diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessageController.java b/modules/core/src/main/java/com/bytedesk/core/message/MessageController.java index 2272aedd64..a82ef1ca5a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessageController.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessageController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-01 17:26:34 + * @LastEditTime: 2024-04-27 16:42:05 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,25 +14,22 @@ */ package com.bytedesk.core.message; -import java.util.Map; - import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.bytedesk.core.utils.JsonResult; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; /** * * http://localhost:9003/swagger-ui/index.html */ -@Slf4j +// @Slf4j @RestController @AllArgsConstructor @RequestMapping("/api/v1/message") @@ -47,28 +44,12 @@ public class MessageController { * @return json */ @GetMapping("/query") - public JsonResult query(MessageRequest messageRequest) { + public ResponseEntity query(MessageRequest messageRequest) { Page messagePage = messageService.query(messageRequest); // - return new JsonResult<>("query message success", 200, messagePage); + return ResponseEntity.ok(JsonResult.success(messagePage)); } - /** - * send offline message - * - * @param map map - * @return json - */ - @PostMapping("/send") - public JsonResult sendOfflineMessage(@RequestBody Map map) { - - String json = (String) map.get("json"); - log.debug("json {}", json); - // stompMqService.sendMessageToMq(json); - // - return new JsonResult<>("send offline message success", 200, json); - } - } diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessageRepository.java b/modules/core/src/main/java/com/bytedesk/core/message/MessageRepository.java index 53324d9062..30e0353177 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessageRepository.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessageRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-01 16:07:01 + * @LastEditTime: 2024-04-17 09:43:34 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -25,7 +25,7 @@ import org.springframework.stereotype.Repository; import io.swagger.v3.oas.annotations.tags.Tag; /** - * + * QuerydslPredicateExecutor */ @Repository @Tag(name = "message - 消息") @@ -35,6 +35,10 @@ public interface MessageRepository extends JpaRepository, JpaSpec Long deleteByMid(String mid); - Page findByThreadsTidIn(String[] threadIds, Pageable pageable); + Page findByThreadsTidIn(String[] threadTids, Pageable pageable); + + Optional findFirstByThreadsTidInOrderByCreatedAtDesc(String[] threadTids); + + boolean existsByMid(String mid); } diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessageResponse.java b/modules/core/src/main/java/com/bytedesk/core/message/MessageResponse.java index 73fdba99ee..a324dd6025 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessageResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessageResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-21 10:00:55 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-04 14:18:57 + * @LastEditTime: 2024-04-22 20:51:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,51 +16,48 @@ package com.bytedesk.core.message; import java.util.Date; -import org.springframework.beans.factory.annotation.Value; - import com.bytedesk.core.rbac.user.UserResponseSimple; -import com.fasterxml.jackson.annotation.JsonFormat; - +import com.bytedesk.core.thread.ThreadResponseSimple; +import com.bytedesk.core.utils.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +/** + * response for visitor init/request thread + * distinguish visitor message from agent view + * 区分 访客端拉取的消息格式 和 客服端拉取到的消息格式 + */ @Data -public class MessageResponse { +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class MessageResponse extends BaseResponse { - @Value("${bytedesk.timezone}") - private static final String timezone = "GMT+8"; + private static final long serialVersionUID = 9911390153L; - private String mid; + /** message */ + private String mid; - /** - * - */ - private String type; + private String type; - /** - * - */ - private String content; + private String content; - /** - * - */ - private String extra; + private String status; - /** - * - */ - private String status; + private String client; - /** - * - */ - private String client; + private Date createdAt; + /** */ + private ThreadResponseSimple thread; - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = timezone) - private Date createdAt; - - - private UserResponseSimple user; + /** */ + private UserResponseSimple user; } diff --git a/modules/core/src/main/java/com/bytedesk/core/message/MessageService.java b/modules/core/src/main/java/com/bytedesk/core/message/MessageService.java index acd77e74f2..030fc00f79 100644 --- a/modules/core/src/main/java/com/bytedesk/core/message/MessageService.java +++ b/modules/core/src/main/java/com/bytedesk/core/message/MessageService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-11 13:54:35 + * @LastEditTime: 2024-04-22 21:11:23 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,11 @@ package com.bytedesk.core.message; import java.util.Optional; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.cache.annotation.CachePut; +// import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -45,19 +50,47 @@ public class MessageService { return messagePage.map(BdConvertUtils::convertToMessageResponse); } + @Cacheable(value = "message", key = "#mid", unless="#result == null") public Optional findByMid(String mid) { return messageRepository.findByMid(mid); } + /** + * find the last message in the thread + * 找到当前会话中最新一条聊天记录 + */ + @Cacheable(value = "message", key = "#threadTid", unless="#result == null") + public Optional findByThreadsTidInOrderByCreatedAtDesc(String threadTid) { + return messageRepository.findFirstByThreadsTidInOrderByCreatedAtDesc(new String[]{threadTid}); + } + + @Caching(put = { + @CachePut(value = "message", key = "#message.mid"), + }) public Message save(@NonNull Message message) { return messageRepository.save(message); } + @Caching(evict = { + @CacheEvict(value = "message", key = "#message.mid"), + }) public void delete(@NonNull Message message) { messageRepository.delete(message); } + @Caching(evict = { + @CacheEvict(value = "message", key = "#mid"), + }) public void deleteByMid(String mid) { messageRepository.deleteByMid(mid); } + + public boolean existsByMid(String mid) { + return messageRepository.existsByMid(mid); + } + + // public MessageResponse convertToMessageResponse(Message message) { + // return modelMapper.map(message, MessageResponse.class); + // } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/schedule/Schedule.java b/modules/core/src/main/java/com/bytedesk/core/openid/OpenId.java similarity index 79% rename from modules/core/src/main/java/com/bytedesk/core/schedule/Schedule.java rename to modules/core/src/main/java/com/bytedesk/core/openid/OpenId.java index b460393f16..83a0dda5d1 100644 --- a/modules/core/src/main/java/com/bytedesk/core/schedule/Schedule.java +++ b/modules/core/src/main/java/com/bytedesk/core/openid/OpenId.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-02-22 16:54:02 + * @Date: 2024-04-09 09:49:09 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 16:54:05 + * @LastEditTime: 2024-04-09 09:49:11 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,11 +12,11 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.schedule; +package com.bytedesk.core.openid; /** - * schedule tasks - 定时任务 + * third party openid, including: wechat/github/google... */ -public class Schedule { - +public class OpenId { + } diff --git a/modules/core/src/main/java/com/bytedesk/core/push/Notifier.java b/modules/core/src/main/java/com/bytedesk/core/push/Notifier.java new file mode 100644 index 0000000000..147f40d7f7 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/Notifier.java @@ -0,0 +1,22 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-03-29 15:49:55 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:55:19 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import com.bytedesk.core.message.Message; + +public abstract class Notifier { + abstract void notify(Message e); + abstract void send(String to, String content); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/Push.java b/modules/core/src/main/java/com/bytedesk/core/push/Push.java index 8817880300..d6485ee28a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/push/Push.java +++ b/modules/core/src/main/java/com/bytedesk/core/push/Push.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-29 15:49:55 + * @Date: 2024-04-25 15:30:11 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 15:49:58 + * @LastEditTime: 2024-04-25 20:23:47 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,53 @@ */ package com.bytedesk.core.push; -public class Push { +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.utils.AuditModel; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * + */ +@Data +@Entity +@Builder +@EqualsAndHashCode(callSuper=false) +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "core_push") +public class Push extends AuditModel { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(unique = true, nullable = false) + private String pid; + + private String sender; + + private String content; + + private String receiver; + + @Column(name = "by_type") + private String type; + + @Builder.Default + private String status = StatusConsts.CODE_STATUS_PENDING; + + private String client; } diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushConfig.java b/modules/core/src/main/java/com/bytedesk/core/push/PushConfig.java new file mode 100644 index 0000000000..6598cde118 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushConfig.java @@ -0,0 +1,40 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-17 10:35:11 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-17 10:54:09 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * async push + * https://www.baeldung.com/spring-async + * https://spring.io/guides/gs/async-method + */ +@EnableAsync +@Configuration +public class PushConfig implements AsyncConfigurer { + + // @Override + // public Executor getAsyncExecutor() { + // ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + // threadPoolTaskExecutor.setCorePoolSize(2); + // threadPoolTaskExecutor.setMaxPoolSize(2); + // threadPoolTaskExecutor.setQueueCapacity(500); + // threadPoolTaskExecutor.setThreadNamePrefix("PushService-"); // default prefix :TaskExecutor- + // threadPoolTaskExecutor.initialize(); + // return threadPoolTaskExecutor; + // } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/SmsService.java b/modules/core/src/main/java/com/bytedesk/core/push/PushController.java similarity index 88% rename from modules/core/src/main/java/com/bytedesk/core/push/SmsService.java rename to modules/core/src/main/java/com/bytedesk/core/push/PushController.java index 9bd9647d5a..0fa198be67 100644 --- a/modules/core/src/main/java/com/bytedesk/core/push/SmsService.java +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushController.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-31 15:29:55 + * @Date: 2024-04-25 15:41:22 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-31 15:29:57 + * @LastEditTime: 2024-04-25 15:41:24 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,6 @@ */ package com.bytedesk.core.push; -public class SmsService { +public class PushController { } diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushRepository.java b/modules/core/src/main/java/com/bytedesk/core/push/PushRepository.java new file mode 100644 index 0000000000..4951569107 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushRepository.java @@ -0,0 +1,31 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:42:24 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 17:36:36 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PushRepository extends JpaRepository { + + List findByStatus(String status); + + Optional findByStatusAndTypeAndReceiver(String status, String type, String receiver); + + Boolean existsByStatusAndTypeAndReceiver(String status, String type, String receiver); + + Boolean existsByStatusAndTypeAndReceiverAndContent(String status, String type, String receiver, String content); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushRequest.java b/modules/core/src/main/java/com/bytedesk/core/push/PushRequest.java new file mode 100644 index 0000000000..407b02f7ad --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushRequest.java @@ -0,0 +1,48 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:42:01 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 20:28:33 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.utils.BaseRequest; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * + */ +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@NoArgsConstructor +public class PushRequest extends BaseRequest { + + private static final long serialVersionUID = 6684752544L; + + private String pid; + + private String sender; + + private String receiver; + + @Builder.Default + private String status = StatusConsts.CODE_STATUS_PENDING; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushResponse.java b/modules/core/src/main/java/com/bytedesk/core/push/PushResponse.java new file mode 100644 index 0000000000..91dab8e4ef --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushResponse.java @@ -0,0 +1,39 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:42:11 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 15:49:46 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import com.bytedesk.core.utils.BaseResponse; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class PushResponse extends BaseResponse { + + private static final long serialVersionUID = -8586294033L; + + private String pid; + + private String content; + + private String type; + + private String status; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushService.java b/modules/core/src/main/java/com/bytedesk/core/push/PushService.java new file mode 100644 index 0000000000..31acbe45fe --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushService.java @@ -0,0 +1,177 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-25 15:41:33 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:24:40 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.exception.EmailExistsException; +import com.bytedesk.core.exception.MobileExistsException; +import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.core.utils.Utils; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class PushService { + + private final PushRepository pushRepository; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + private final PushServiceImplEmail pushServiceImplEmail; + + private final PushServiceImplSms pushServiceImplSms; + + private final UserService userService; + + public Boolean sendEmailCode(String email, String client, String authType) { + + return sendCode(email, TypeConsts.TYPE_EMAIL, client, authType); + } + + public Boolean sendSmsCode(String mobile, String client, String authType) { + + return sendCode(mobile, TypeConsts.TYPE_MOBILE, client, authType); + } + + public Boolean sendCode(String receiver, String type, String client, String authType) { + + // 注册验证码,如果账号已经存在,则直接抛出异常 + if (authType.equals(TypeConsts.SEND_MOBILE_CODE_TYPE_REGISTER)) { + + if (type.equals(TypeConsts.TYPE_MOBILE) && userService.existsByMobile(receiver)) { + throw new MobileExistsException("mobile already exists"); + } + + if (type.equals(TypeConsts.TYPE_EMAIL) && userService.existsByEmail(receiver)) { + throw new EmailExistsException("email already exists"); + } + } + + // check if has already send validate code within 15min + if (existsByStatusAndTypeAndReceiver(StatusConsts.CODE_STATUS_PENDING, type, receiver)) { + return false; + } + + String code = Utils.getRandomCode(receiver); + + if (type.equals(TypeConsts.TYPE_EMAIL)) { + pushServiceImplEmail.send(receiver, code); + } else if (type.equals(TypeConsts.TYPE_MOBILE)) { + pushServiceImplSms.send(receiver, code); + } else { + return false; + } + + // + PushRequest pushRequest = new PushRequest(); + pushRequest.setType(type); + pushRequest.setSender(TypeConsts.TYPE_SYSTEM); + pushRequest.setContent(code); + pushRequest.setReceiver(receiver); + pushRequest.setClient(client); + create(pushRequest); + + return true; + } + + public Push create(PushRequest pushRequest) { + log.info("pushRequest {}", pushRequest.toString()); + + Push push = modelMapper.map(pushRequest, Push.class); + push.setPid(uidUtils.getCacheSerialUid()); + push.setClient(pushRequest.getClient()); + + return save(push); + } + + public Boolean validateEmailCode(String email, String code) { + return validateCode(email, TypeConsts.TYPE_EMAIL, code); + } + + public Boolean validateSmsCode(String mobile, String code) { + return validateCode(mobile, TypeConsts.TYPE_MOBILE, code); + } + + public Boolean validateCode(String receiver, String type, String code) { + return pushRepository.existsByStatusAndTypeAndReceiverAndContent(StatusConsts.CODE_STATUS_PENDING, type, receiver, code); + } + + @Cacheable(value = "push", key = "#receiver-#status-#type", unless = "#result == null") + public Optional findByStatusAndTypeAndReceiver(String status, String type, String receiver) { + return pushRepository.findByStatusAndTypeAndReceiver(status, type, receiver); + } + + public Boolean existsByStatusAndTypeAndReceiver(String status, String type, String receiver) { + return pushRepository.existsByStatusAndTypeAndReceiver(status, type, receiver); + } + + @Caching(put = { + @CachePut(value = "push", key = "#push.receiver"), + // TODO: 根据status, 缓存或清空缓存,clear or cache according to status + }) + public Push save(Push push) { + return pushRepository.save(push); + } + + // TODO: 更新缓存 + @Cacheable(value = "pushPending") + public List findStatusPending() { + return pushRepository.findByStatus(StatusConsts.CODE_STATUS_PENDING); + } + + // 自动过期 + // TODO: 频繁查库,待优化 + @Async + public void autoOutdateCode() { + List pendingPushes = findStatusPending(); + pendingPushes.forEach(push -> { + // 计算两个日期之间的毫秒差 + long diffInMilliseconds = Math.abs(new Date().getTime() - push.getUpdatedAt().getTime()); + // 转换为分钟 + long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(diffInMilliseconds); + // 验证码有效时间15分钟 + if (diffInMinutes > 15) { + // TODO: 过期,清空缓存 + push.setStatus(StatusConsts.CODE_STATUS_EXPIRED); + save(push); + } + }); + } + + + public PushResponse convertToPushResponse(Push push) { + return modelMapper.map(push, PushResponse.class); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplAndroid.java b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplAndroid.java new file mode 100644 index 0000000000..96622596a6 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplAndroid.java @@ -0,0 +1,39 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-03-31 15:30:30 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:55:50 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.message.Message; + +@Service +public class PushServiceImplAndroid extends Notifier { + + + @Async + @Override + void notify(Message e) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'notify'"); + } + + @Override + void send(String to, String content) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'send'"); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplEmail.java b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplEmail.java new file mode 100644 index 0000000000..71029f2e6f --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplEmail.java @@ -0,0 +1,44 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-03-31 15:30:19 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:55:58 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.message.Message; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class PushServiceImplEmail extends Notifier { + + + @Async + @Override + void notify(Message e) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'notify'"); + } + + @Async + @Override + void send(String to, String content) { + log.info("TODO: send email to {}, content {}", to, content); + + + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplIos.java b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplIos.java new file mode 100644 index 0000000000..60126d0824 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplIos.java @@ -0,0 +1,38 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-03-31 15:30:08 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:55:38 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.message.Message; + +@Service +public class PushServiceImplIos extends Notifier { + + @Async + @Override + void notify(Message e) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'notify'"); + } + + @Override + void send(String to, String content) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'send'"); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplSms.java b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplSms.java new file mode 100644 index 0000000000..e775fd6eae --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/push/PushServiceImplSms.java @@ -0,0 +1,43 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-03-31 15:29:55 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 12:56:13 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.push; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.bytedesk.core.message.Message; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class PushServiceImplSms extends Notifier { + + @Async + @Override + void notify(Message e) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'notify'"); + } + + @Async + @Override + void send(String to, String content) { + // TODO Auto-generated method stub + log.info("TODO: send sms to {}, content: {}", to, content); + + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionController.java b/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionController.java deleted file mode 100644 index 47a2747346..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionController.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.bytedesk.core.rbac.action; - -import lombok.AllArgsConstructor; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * 操作:查询、插入、删除 - */ -// @Slf4j -@AllArgsConstructor -@RestController -@RequestMapping("/api/v1/action") -public class ActionController { - - // private final ActionService actionService; - - /** - * - * - * @return json - */ - // @GetMapping("/list") - // public JsonResult list(@RequestParam(value = "client") final String client) { - // // - // final List actionDTOS = actionService.findAllDTO(); - - // return new JsonResult<>("获取操作列表成功", 200, actionDTOS); - // } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRepository.java b/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRepository.java deleted file mode 100644 index 01bae0d908..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:24 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-01-31 11:26:39 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedeskt - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.rbac.action; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import io.swagger.v3.oas.annotations.tags.Tag; - -import java.util.Optional; - -/** - * action - */ -@Repository -@Tag(name = "actions - 操作") -public interface ActionRepository extends JpaRepository { - - Optional findByValue(String value); -} diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthController.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthController.java similarity index 55% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthController.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthController.java index ef7391015f..a1b4aeb3b0 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthController.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-01 15:58:08 + * @LastEditTime: 2024-04-26 12:11:26 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; @@ -23,17 +23,16 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.bytedesk.core.annotation.ActionLog; +import com.bytedesk.core.push.PushService; import com.bytedesk.core.rbac.user.UserRequest; import com.bytedesk.core.rbac.user.UserResponse; import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.redis.RedisLoginService; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -// import com.bytedesk.core.token.RefreshToken; -// import com.bytedesk.core.token.RefreshTokenService; import com.bytedesk.core.utils.JsonResult; /** @@ -50,10 +49,10 @@ public class AuthController { private AuthService authService; - private RedisLoginService redisLoginService; - private AuthenticationManager authenticationManager; + private PushService pushService; + /** * * @param userRequest @@ -61,15 +60,19 @@ public class AuthController { */ @PostMapping(value = "/register") public ResponseEntity register(@RequestBody UserRequest userRequest) { - try { - UserResponse userResponse = userService.register(userRequest); - return ResponseEntity.ok(JsonResult.success(userResponse)); - } catch (Exception e) { - throw new RuntimeException(e); + // validate sms code + // 验证手机验证码 + if (!pushService.validateSmsCode(userRequest.getMobile(), userRequest.getCode())) { + return ResponseEntity.ok().body(JsonResult.error("validate code failed", -1, false)); } + + UserResponse userResponse = userService.register(userRequest); + + return ResponseEntity.ok(JsonResult.success("register success", userResponse)); } + @ActionLog(title = "Auth", action = "loginWithUsernamePassword", description = "Login With Username & Password") @PostMapping("/login") public ResponseEntity loginWithUsernamePassword(@RequestBody AuthRequest authRequest) { log.debug("login {}", authRequest.toString()); @@ -77,23 +80,22 @@ public class AuthController { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())); // - return authService.formatResponse(authentication); + AuthResponse authResponse = authService.formatResponse(authentication); + + return ResponseEntity.ok(JsonResult.success(authResponse)); } @PostMapping("/send/mobile") public ResponseEntity sendMobileCode(@RequestBody AuthRequest authRequest) { - log.debug("send mobile code {}", authRequest.toString()); + log.debug("send mobile code {}, client {}, type {}", authRequest.toString(), authRequest.getClient(), authRequest.getType()); - // check if has already send validate code within 15min - if (redisLoginService.hasValidateCode(authRequest.getMobile())) { - return ResponseEntity.ok() - .body(new JsonResult<>("validate code already sent", -1, false)); + // send mobile code + Boolean result = pushService.sendSmsCode(authRequest.getMobile(), authRequest.getClient(), authRequest.getType()); + if (!result) { + return ResponseEntity.ok().body(JsonResult.error("already send, dont repeat", -1, false)); } - int code = authService.getRandomCode(authRequest.getMobile()); - // TODO: send mobile code - - return ResponseEntity.ok().body(new JsonResult<>("validate code success", 200, code)); + return ResponseEntity.ok().body(JsonResult.success("send mobile code success")); } @PostMapping("/login/mobile") @@ -101,36 +103,67 @@ public class AuthController { log.debug("login mobile {}", authRequest.toString()); // validate mobile & code - if (!redisLoginService.validateCode(authRequest.getMobile(), authRequest.getCode())) { - return ResponseEntity.ok() - .body(new JsonResult<>("validate code failed", -1, false)); - + // 验证手机验证码 + if (!pushService.validateSmsCode(authRequest.getMobile(), authRequest.getCode())) { + return ResponseEntity.ok().body(JsonResult.error("validate code failed", -1, false)); } + + // if mobile already exists, if none, then registe + // 手机号是否已经注册,如果没有,则自动注册 + if (!userService.existsByMobile(authRequest.getMobile())) { + UserRequest userRequest = new UserRequest(); + userRequest.setUsername(authRequest.getMobile()); + userRequest.setMobile(authRequest.getMobile()); + userService.register(userRequest); + } + Authentication authentication = authService.authenticationWithMobile(authRequest.getMobile()); // - return authService.formatResponse(authentication); + AuthResponse authResponse = authService.formatResponse(authentication); + + return ResponseEntity.ok(JsonResult.success(authResponse)); } @PostMapping("/send/email") public ResponseEntity sendEmailCode(@RequestBody AuthRequest authRequest) { log.debug("send email code {}", authRequest.toString()); - // TODO: send email code + // send email code + Boolean result = pushService.sendEmailCode(authRequest.getEmail(), authRequest.getClient(), authRequest.getType()); + if (!result) { + return ResponseEntity.ok(JsonResult.error("already send, dont repeat", -1, false)); + } - return ResponseEntity.ok().body(null); + return ResponseEntity.ok(JsonResult.success("send email code success")); } @PostMapping("/login/email") public ResponseEntity loginWithEmailCode(@RequestBody AuthRequest authRequest) { log.debug("login email {}", authRequest.toString()); - // TODO: validate email & code + // validate email & code + if (!pushService.validateEmailCode(authRequest.getEmail(), authRequest.getCode())) { + return ResponseEntity.ok(JsonResult.error("validate code failed", -1, false)); + } + + // 邮箱是否已经注册,如果没有,则自动注册 + if (!userService.existsByEmail(authRequest.getEmail())) { + UserRequest userRequest = new UserRequest(); + userRequest.setUsername(authRequest.getEmail()); + userRequest.setEmail(authRequest.getEmail()); + userService.register(userRequest); + } Authentication authentication = authService.authenticationWithEmail(authRequest.getEmail()); // - return authService.formatResponse(authentication); + AuthResponse authResponse = authService.formatResponse(authentication); + + return ResponseEntity.ok(JsonResult.success(authResponse)); } + + + // @PostMapping("/refreshToken") // public JwtResponse refreshToken(@RequestBody RefreshTokenRequest // refreshTokenRequestDTO) { diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthEntryPoint.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthEntryPoint.java similarity index 98% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthEntryPoint.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthEntryPoint.java index e3362a4d5e..10654520a0 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthEntryPoint.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthEntryPoint.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; import java.io.IOException; import java.util.HashMap; diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthJwtTokenFilter.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthJwtTokenFilter.java similarity index 72% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthJwtTokenFilter.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthJwtTokenFilter.java index 5f2da94b44..4cfd1ade15 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthJwtTokenFilter.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthJwtTokenFilter.java @@ -1,12 +1,18 @@ /* - * @Author: jack ning github@bytedesk.com - * @Date: 2024-01-29 11:48:06 + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:17:36 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 11:35:54 - * @FilePath: /spring-boot-spring-security-jwt-authentication/src/main/java/com/bezkoder/springjwt/security/jwt/AuthTokenFilter.java - * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + * @LastEditTime: 2024-04-27 10:19:53 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; import java.io.IOException; diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthRequest.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthRequest.java similarity index 76% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthRequest.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthRequest.java index 83baade34b..dd63a3032a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-23 07:53:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-03 10:41:05 + * @LastEditTime: 2024-04-25 20:28:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,16 +12,22 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; + +import com.bytedesk.core.utils.BaseRequest; import lombok.Data; +import lombok.EqualsAndHashCode; /** * @author 20580156@qq.com * @project bytedesk-im */ @Data -public class AuthRequest { +@EqualsAndHashCode(callSuper=false) +public class AuthRequest extends BaseRequest { + + private static final long serialVersionUID = -9618212342L; private String username; private String password; diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthResponse.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthResponse.java similarity index 96% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthResponse.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthResponse.java index 50d5253952..5ad22a1e00 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthResponse.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; import com.bytedesk.core.rbac.user.UserResponse; diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthService.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthService.java similarity index 72% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthService.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthService.java index 039937bc0e..b911925ab1 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthService.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-23 07:53:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 21:28:12 + * @LastEditTime: 2024-04-27 10:40:14 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,11 +12,9 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; -import java.util.Random; import org.modelmapper.ModelMapper; -import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -29,8 +27,6 @@ import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserDetailsImpl; import com.bytedesk.core.rbac.user.UserDetailsServiceImpl; import com.bytedesk.core.rbac.user.UserResponse; -import com.bytedesk.core.redis.RedisLoginService; -import com.bytedesk.core.utils.JsonResult; import com.bytedesk.core.utils.JwtUtils; import jakarta.servlet.http.HttpServletRequest; @@ -47,8 +43,6 @@ public class AuthService { private JwtUtils jwtUtils; - private RedisLoginService redisLoginService; - private UserDetailsServiceImpl userDetailsService; private ModelMapper modelMapper; @@ -82,49 +76,28 @@ public class AuthService { return new AuthToken(userDetails); } - public ResponseEntity formatResponse(Authentication authentication) { + public AuthResponse formatResponse(Authentication authentication) { SecurityContextHolder.getContext().setAuthentication(authentication); UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); - // List roles = userDetails.getAuthorities().stream() - // .map(item -> item.getAuthority()) - // .collect(Collectors.toList()); - UserResponse userResponse = modelMapper.map(userDetails, UserResponse.class); + + UserResponse userResponse = convertToUserResponse(userDetails); + String jwt = jwtUtils.generateJwtToken(userDetails.getUsername()); - return ResponseEntity.ok(new JsonResult<>("login success", 200, AuthResponse.builder() + return AuthResponse.builder() .access_token(jwt) .user(userResponse) - .build())); - } - - /** - * for test - * - * @param mobile - * @return - */ - public boolean isTestMobile(String mobile) { - return mobile.startsWith("133111562") || mobile.startsWith("1888888"); - } - - /** - * - * @return - */ - @SuppressWarnings("null") - public int getRandomCode(String key) { - int code = 123456; - if (isTestMobile(key)) { - code = 123456; - } else { - int min = 100001; - int max = 999998; - code = new Random().nextInt(max) % (max - min + 1) + min; - } - redisLoginService.cacheValidateCode(key, String.valueOf(code)); - return code; + .build(); + } + + + public UserResponse convertToUserResponse(UserDetailsImpl userDetails) { + UserResponse userResponse = modelMapper.map(userDetails, UserResponse.class); + + return userResponse; } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/auth/AuthToken.java b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthToken.java similarity index 98% rename from modules/core/src/main/java/com/bytedesk/core/auth/AuthToken.java rename to modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthToken.java index 632f22c57f..569085613a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/auth/AuthToken.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/auth/AuthToken.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.auth; +package com.bytedesk.core.rbac.auth; import java.util.Collection; diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/Authority.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/Authority.java index f9581d2fd0..c88dfaa829 100755 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/Authority.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/Authority.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:01:47 + * @LastEditTime: 2024-04-24 11:13:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,28 +14,30 @@ */ package com.bytedesk.core.rbac.authority; -import com.bytedesk.core.rbac.action.Action; -import com.fasterxml.jackson.annotation.JsonIgnore; - +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import jakarta.persistence.*; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; + +import com.bytedesk.core.utils.AuditModel; /** * @author im.bytedesk.com */ @Data +@Builder @Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) @Entity +@AllArgsConstructor +@NoArgsConstructor @Table(name = "core_authority") -// @JsonIgnoreProperties(value = { "handler", "hibernateLazyInitializer", -// "fieldHandler" }) -public class Authority implements Serializable { +public class Authority extends AuditModel { - private static final long serialVersionUID = 8868352778004638978L; + private static final long serialVersionUID = -3232924689L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -46,25 +48,19 @@ public class Authority implements Serializable { private String name; + /** value is a keyword in h2 db */ + @Column(name = "by_value") private String value; - private Integer status = 1; + // private Integer status = 1; private String description; @Column(name = "by_type") private String type; - @JsonIgnore - @OneToMany - private List actions = new ArrayList<>(); - // @JsonIgnore - // @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) - // @JoinTable(name = "core_authority_actions", - // joinColumns = @JoinColumn(name = "authority_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)), - // inverseJoinColumns = @JoinColumn(name = "action_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) - // ) - // private List actions; + // @OneToMany + // private List actions = new ArrayList<>(); } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityController.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityController.java index 612633713e..1a0ff8849c 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityController.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityController.java @@ -1,28 +1,9 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - */ package com.bytedesk.core.rbac.authority; import lombok.AllArgsConstructor; -import java.util.List; - -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import com.bytedesk.core.utils.JsonResult; - /** * 权限管理 */ @@ -32,38 +13,20 @@ import com.bytedesk.core.utils.JsonResult; @RequestMapping("/api/v1/authority") public class AuthorityController { - private final AuthorityService authorityService; - - /** - * 全部 - * - * @return json - */ - // @GetMapping("/list") - // public Callable> list() { - - // return () -> { - - // // 全部查询 - // List authorityDTOPage = authorityService.findByUser(); - - // return new JsonResult<>("获取全部权限成功", 200, authorityDTOPage); - // }; - // } + // private final AuthorityService authorityService; /** * 查询 * * @return json */ - @GetMapping("/query") - public ResponseEntity> query(AuthorityRequest authorityRequest) { + // @GetMapping("/query") + // public ResponseEntity> query(AuthorityRequest authorityRequest) { + // List authorityList = authorityService.findAll(); - List authorityList = authorityService.findAll(); - - return ResponseEntity.ok().body(new JsonResult<>("get authority success", 200, authorityList)); - } + // return ResponseEntity.ok().body(new JsonResult<>("get authority success", 200, authorityList)); + // } /** * 创建 diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRepository.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRepository.java index f482290bd2..e3804538af 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRepository.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-01-31 11:27:43 + * @LastEditTime: 2024-04-24 11:04:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk@ * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -31,13 +31,7 @@ import java.util.Optional; @Tag(name = "authoritys - 权限") public interface AuthorityRepository extends JpaRepository, JpaSpecificationExecutor { - // List findByUser(User user); - - // Page findByUser(User user, Pageable pageable); - - // Page findByNameContainingOrValueContainingAndUser(String name, - // String value, User user, - // Pageable pageable); + Optional findByAid(String aid); Optional findByValue(String value); diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRequest.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRequest.java index e2248d9fd6..0a0f602b8c 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:02:47 + * @LastEditTime: 2024-04-24 10:41:37 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,14 +14,16 @@ */ package com.bytedesk.core.rbac.authority; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; - -import java.util.List; +import lombok.experimental.Accessors; import com.bytedesk.core.utils.BaseRequest; @Data +@Builder +@Accessors(chain = true) @EqualsAndHashCode(callSuper = true) public class AuthorityRequest extends BaseRequest { @@ -31,12 +33,10 @@ public class AuthorityRequest extends BaseRequest { private String value; - private int status; + // private int status; private String description; - private String type; - - private List actions; + // private List actions; } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityResponse.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityResponse.java index d76d7f9898..13df5433dc 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:02:57 + * @LastEditTime: 2024-04-24 11:19:54 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,9 +17,6 @@ package com.bytedesk.core.rbac.authority; import lombok.Data; import lombok.EqualsAndHashCode; -import java.util.List; - -import com.bytedesk.core.rbac.action.ActionResponse; import com.bytedesk.core.utils.BaseResponse; @Data @@ -32,12 +29,12 @@ public class AuthorityResponse extends BaseResponse { private String value; - private int status; + // private int status; private String description; private String type; - private List actions; + // private List actions; } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityService.java b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityService.java index a3fd62e402..4e2a796d7b 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityService.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/authority/AuthorityService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-06 09:25:50 + * @LastEditTime: 2024-04-24 10:54:51 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,20 +15,77 @@ package com.bytedesk.core.rbac.authority; import lombok.AllArgsConstructor; + +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import java.util.List; +import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.uid.UidUtils; + +import java.util.Optional; @Service @AllArgsConstructor public class AuthorityService { - // private final ActionRepository actionRepository; - private final AuthorityRepository authorityRepository; - public List findAll() { - return authorityRepository.findAll(); + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + public Authority create(AuthorityRequest authorityRequest) { + // + Authority authority = modelMapper.map(authorityRequest, Authority.class); + authority.setAid(uidUtils.getCacheSerialUid()); + + return save(authority); + } + + @Cacheable(value = "authority", key = "#aid", unless = "#result == null") + public Optional findByAid(String aid) { + return authorityRepository.findByAid(aid); + } + + @Cacheable(value = "authority", key = "#value", unless = "#result == null") + public Optional findByValue(String value) { + return authorityRepository.findByValue(value); + } + + public Authority save(Authority authority) { + return authorityRepository.save(authority); + } + + public void initData() { + + if (authorityRepository.count() > 0) { + return; + } + // + String[] authorities = { + TypeConsts.AUTHORITY_SUPER, + TypeConsts.AUTHORITY_ADMIN, + TypeConsts.AUTHORITY_HR, + TypeConsts.AUTHORITY_ORG, + TypeConsts.AUTHORITY_IT, + TypeConsts.AUTHORITY_MONEY, + TypeConsts.AUTHORITY_MARKETING, + TypeConsts.AUTHORITY_SALES, + TypeConsts.AUTHORITY_CUSTOMER_SERVICE, + }; + + for (String authority : authorities) { + AuthorityRequest authRequest = AuthorityRequest.builder() + .name(authority) + .value(authority) + .description(authority) + .build(); + authRequest.setType(TypeConsts.TYPE_SYSTEM); + // + create(authRequest); + } + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/Role.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/Role.java index d6ba569f2a..f190cf880c 100755 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/Role.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/Role.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 18:12:28 + * @LastEditTime: 2024-04-24 11:06:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,21 +14,20 @@ */ package com.bytedesk.core.rbac.role; -import com.bytedesk.core.rbac.authority.Authority; -import com.bytedesk.core.rbac.user.User; -import com.fasterxml.jackson.annotation.JsonBackReference; -import com.fasterxml.jackson.annotation.JsonIgnore; - import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; +import java.util.Set; +import java.util.HashSet; + +import com.bytedesk.core.rbac.authority.Authority; +import com.bytedesk.core.utils.AuditModel; +import com.fasterxml.jackson.annotation.JsonIgnore; /** * role table @@ -38,13 +37,14 @@ import java.util.List; @Data @Entity @Builder +@EqualsAndHashCode(callSuper=false) @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor @Table(name = "core_role") -public class Role implements Serializable { +public class Role extends AuditModel { - private static final long serialVersionUID = -2461905686314283608L; + private static final long serialVersionUID = -1470818087L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -53,47 +53,41 @@ public class Role implements Serializable { @Column(unique = true, nullable = false, length = 127) private String rid; - /** - * name - */ private String name; /** - * value + * value is a keyword in h2 db */ - @Column(unique = true, nullable = false) - private String value; + // @Column(name = "by_value", unique = true, nullable = false) + // private String value; - /** - * - */ private String description; - /** - * - */ @Column(name = "by_type") private String type; - /** - * - */ @JsonIgnore @Builder.Default - @OneToMany - private List authorities = new ArrayList<>(); - // @JsonIgnore - // @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) - // @JoinTable(name = "core_role_authorities", joinColumns = @JoinColumn(name = "role_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)), inverseJoinColumns = @JoinColumn(name = "authority_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))) - // private List authorities; + @ManyToMany + private Set authorities = new HashSet<>(); /** * 除系统自带角色之外, * 允许管理员-自己创建角色 */ - @JsonIgnore - @ManyToOne(fetch = FetchType.LAZY) - @JsonBackReference // 避免无限递归 - private User user; + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // @JsonBackReference("user-roles") // 避免无限递归 + // private User user; + + private String orgOid; + + + public void addAuthority(Authority authority) { + this.authorities.add(authority); + } + public void removeAuthority(Authority authority) { + this.authorities.remove(authority); + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleController.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleController.java index 93ee11e71d..300fff10a0 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleController.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleController.java @@ -22,8 +22,6 @@ import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import com.bytedesk.core.auth.AuthService; -import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.JsonResult; /** @@ -37,7 +35,7 @@ public class RoleController { private final RoleService roleService; - private final AuthService authService; + // private final AuthService authService; /** * query my roles by page @@ -47,9 +45,7 @@ public class RoleController { @GetMapping("/query") public ResponseEntity> query(RoleRequest roleRequest) { - User user = authService.getCurrentUser(); - - Page rolePage = roleService.query(user, roleRequest); + Page rolePage = roleService.query(roleRequest); // return ResponseEntity.ok(JsonResult.success(rolePage)); } @@ -76,9 +72,7 @@ public class RoleController { */ // @PostMapping("/update") // public Callable> update(@RequestBody RoleParam roleParam) { - // return () -> { - // RoleDTO roleDTO = roleService.update(roleParam); // // // return new JsonResult<>("更新角色成功", 200, roleDTO); @@ -93,9 +87,7 @@ public class RoleController { */ // @PostMapping("/delete") // public Callable> delete(@RequestBody RoleParam roleParam) { - // return () -> { - // // // roleService.deleteById(roleParam.getId()); // return new JsonResult<>("删除角色成功", 200, roleParam.getId()); @@ -109,9 +101,7 @@ public class RoleController { */ // @GetMapping("/filter") // public Callable> filter(FilterParam filterParam) { - // return () -> { - // // // Page roleDTOPage = roleService.findByNameContainingOrValueContainingAndUser(filterParam); // // // return new JsonResult<>("搜索角色成功", 200, roleDTOPage); diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRepository.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRepository.java index 91f2d56a06..80be87eb7d 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRepository.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 15:46:34 + * @LastEditTime: 2024-04-24 10:14:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -22,8 +22,6 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import com.bytedesk.core.rbac.user.User; - import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; @@ -38,13 +36,14 @@ import java.util.Optional; @Tag(name = "roles - 角色") public interface RoleRepository extends JpaRepository, JpaSpecificationExecutor { - Optional findByValue(String value); + Optional findByName(String name); List findByType(String type); - Page findByUser(User user, Pageable pageable); + // Page findByUser(User user, Pageable pageable); + Page findByOrgOid(String orgOid, Pageable pageable); - Boolean existsByValue(String value); + Boolean existsByName(String name); @Transactional void deleteById(@NonNull Long id); diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRequest.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRequest.java index 8138c6e658..afad4d3c50 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:00:05 + * @LastEditTime: 2024-04-24 11:10:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,14 +14,19 @@ */ package com.bytedesk.core.rbac.role; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import java.util.HashSet; import java.util.Set; import com.bytedesk.core.utils.BaseRequest; @Data +@Builder +@Accessors(chain = true) @EqualsAndHashCode(callSuper = false) public class RoleRequest extends BaseRequest { @@ -29,12 +34,16 @@ public class RoleRequest extends BaseRequest { private String name; - private String value; + // private String value; private String description; - // private String type; + @Builder.Default + private Set authorityAids = new HashSet<>(); - private Set authorities; + // organization oid + private String orgOid; + + } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleResponse.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleResponse.java index c0171847ba..faf0289e12 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 14:45:38 + * @LastEditTime: 2024-04-24 11:16:29 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,7 +21,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import com.bytedesk.core.rbac.authority.AuthorityResponse; import com.bytedesk.core.utils.BaseResponse; @@ -37,11 +38,13 @@ import com.bytedesk.core.utils.BaseResponse; @EqualsAndHashCode(callSuper = true) public class RoleResponse extends BaseResponse { + private static final long serialVersionUID = 4082434575L; + private String rid; private String name; - private String value; + // private String value; private String description; @@ -51,6 +54,7 @@ public class RoleResponse extends BaseResponse { */ private String type; - private List authorities; + @Builder.Default + private Set authorities = new HashSet<>(); } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleService.java b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleService.java index 11d83e3c52..549fdc96be 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleService.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/role/RoleService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 11:07:32 + * @LastEditTime: 2024-04-27 12:55:37 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,8 +15,12 @@ package com.bytedesk.core.rbac.role; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -25,16 +29,19 @@ import org.springframework.stereotype.Service; import com.bytedesk.core.config.BytedeskProperties; import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.rbac.authority.Authority; +import com.bytedesk.core.rbac.authority.AuthorityService; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserRepository; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.BdConvertUtils; -import com.bytedesk.core.utils.Utils; import java.util.Arrays; +import java.util.Iterator; import java.util.Optional; @AllArgsConstructor -@Slf4j +// @Slf4j @Service public class RoleService { @@ -44,97 +51,119 @@ public class RoleService { private final BytedeskProperties properties; - public Page query(User user, RoleRequest roleRequest) { - - Pageable pageable = PageRequest.of(roleRequest.getPageNumber(), roleRequest.getPageSize(), Sort.Direction.DESC, + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + private final AuthorityService authorityService; + + public Role create(RoleRequest rolerRequest) { + + if (existsByName(rolerRequest.getName())) { + throw new RuntimeException("角色已存在"); + } + + Role role = modelMapper.map(rolerRequest, Role.class); + role.setRid(uidUtils.getCacheSerialUid()); + // + Iterator iterator = rolerRequest.getAuthorityAids().iterator(); + while (iterator.hasNext()) { + String authorityAid = iterator.next(); + Optional authorityOptional = authorityService.findByAid(authorityAid); + if (authorityOptional.isPresent()) { + role.addAuthority(authorityOptional.get()); + } + } + // + return save(role); + } + + public Page query(RoleRequest roleRequest) { + + Pageable pageable = PageRequest.of(roleRequest.getPageNumber(), roleRequest.getPageSize(), + Sort.Direction.DESC, "id"); - - Page rolePage = roleRepository.findByUser(user, pageable); + + Page rolePage = roleRepository.findByOrgOid(roleRequest.getOrgOid(), pageable); return rolePage.map(BdConvertUtils::convertToRoleResponse); } - - public Optional findByValue(String value) { - return roleRepository.findByValue(value); + + @Cacheable(value = "roleExists", key = "#name", unless="#result == null") + public Boolean existsByName(String namString) { + return roleRepository.existsByName(namString); } - private static Role[] roles = new Role[] { - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_ADMIN).value( - TypeConsts.ROLE_ADMIN) - .description( - TypeConsts.ROLE_ADMIN) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_ORG).value( - TypeConsts.ROLE_ORG) - .description( - TypeConsts.ROLE_ORG) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_IT).value( - TypeConsts.ROLE_IT) - .description( - TypeConsts.ROLE_ADMIN) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_MONEY).value( - TypeConsts.ROLE_MONEY) - .description( - TypeConsts.ROLE_MONEY) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_HR).value( - TypeConsts.ROLE_HR) - .description( - TypeConsts.ROLE_HR) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_MARKETING).value( - TypeConsts.ROLE_MARKETING) - .description( - TypeConsts.ROLE_MARKETING) - .type(TypeConsts.TYPE_SYSTEM).build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_SALES).value( - TypeConsts.ROLE_SALES) - .description(TypeConsts.ROLE_SALES).type(TypeConsts.TYPE_SYSTEM) - .build(), - Role.builder().rid(Utils.getUid()).name( - TypeConsts.ROLE_CUSTOMER_SERVICE).value( - TypeConsts.ROLE_CUSTOMER_SERVICE) - .description(TypeConsts.ROLE_CUSTOMER_SERVICE) - .type(TypeConsts.TYPE_SYSTEM).build(), + @Cacheable(value = "role", key = "#name", unless="#result == null") + public Optional findByName(String name) { + return roleRepository.findByName(name); + } + @Caching(put = { + @CachePut(value = "role", key = "#role.name"), + // TODO: 此处put的exists内容跟缓存时内容类型是否一致? + // @CachePut(value = "roleExists", key = "#role.name") + }) + public Role save(Role role) { + return roleRepository.save(role); + } + + // + // private static final String [] roles = { + // TypeConsts.ROLE_ADMIN, + // TypeConsts.ROLE_ORG, + // TypeConsts.ROLE_IT, + // TypeConsts.ROLE_MONEY, + // TypeConsts.ROLE_HR, + // TypeConsts.ROLE_MARKETING, + // TypeConsts.ROLE_SALES, + // TypeConsts.ROLE_CUSTOMER_SERVICE, + // }; + // + private static final String[] authorities = { + TypeConsts.AUTHORITY_SUPER, + TypeConsts.AUTHORITY_ADMIN, + TypeConsts.AUTHORITY_HR, + TypeConsts.AUTHORITY_ORG, + TypeConsts.AUTHORITY_IT, + TypeConsts.AUTHORITY_MONEY, + TypeConsts.AUTHORITY_MARKETING, + TypeConsts.AUTHORITY_SALES, + TypeConsts.AUTHORITY_CUSTOMER_SERVICE, }; public void initData() { if (roleRepository.count() > 0) { - log.debug("role already exists"); + // log.debug("role already exists"); return; } // - Arrays.stream(roles).forEach((role) -> { - if (!roleRepository.existsByValue(role.getValue())) { - roleRepository.save(role); + for (String authority : authorities) { + String role = TypeConsts.ROLE_ + authority; + RoleRequest roleRequest = RoleRequest.builder().name(role).description(role).build(); + roleRequest.setType(TypeConsts.TYPE_SYSTEM); + // + Optional authorityOptional = authorityService.findByValue(authority); + if (authorityOptional.isPresent()) { + roleRequest.getAuthorityAids().add(authorityOptional.get().getAid()); } - }); + // + create(roleRequest); + } } - @SuppressWarnings("null") public void updateInitData() { - - // Optional adminOptional = userRepository.findByEmail(properties.getEmail()); if (adminOptional.isPresent()) { // - Arrays.stream(roles).forEach((role) -> { - Optional roleOptional = roleRepository.findByValue(role.getValue()); - if (roleOptional.isPresent()) { - roleOptional.get().setUser(adminOptional.get()); - roleRepository.save(roleOptional.get()); - } + Arrays.stream(authorities).forEach((authority) -> { + String roleName = TypeConsts.ROLE_ + authority; + Optional roleOptional = findByName(roleName); + roleOptional.ifPresent(role -> { + role.setOrgOid(adminOptional.get().getOrgOid()); + roleRepository.save(role); + }); }); } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/User.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/User.java index 9dcd0a6346..6c294931d6 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/User.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/User.java @@ -1,27 +1,26 @@ package com.bytedesk.core.rbac.user; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.rbac.role.Role; import com.bytedesk.core.utils.AuditModel; -import com.bytedesk.core.utils.StringListConverter; +import com.bytedesk.core.utils.StringSetConverter; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonManagedReference; - import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; +import jakarta.persistence.ManyToMany; import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.validation.constraints.Digits; +// import jakarta.persistence.UniqueConstraint; +// import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.Email; import lombok.AllArgsConstructor; import lombok.Builder; @@ -37,45 +36,41 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor +@EntityListeners({ UserListener.class }) @Table(name = "core_user", uniqueConstraints = { - @UniqueConstraint(columnNames = "username"), - @UniqueConstraint(columnNames = "email") + // @UniqueConstraint(columnNames = "username"), + // @UniqueConstraint(columnNames = "email") }) public class User extends AuditModel { + private static final long serialVersionUID = 3817197261L; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - /** - * uuid - */ - @Column(name = "uuid", unique = true, nullable = false, length = 127) + @Column(name = "uuid", unique = true, nullable = false) private String uid; - /** - * - */ @Column(unique = true) private String num; - /** - * - */ + // used in authjwtToken, should not be null @Column(unique = true, nullable = false) private String username; private String nickname; @JsonIgnore - @Column(nullable = false) private String password; @Email(message = "email format error") @Column(unique = true) private String email; - @Digits(message = "phone length error", fraction = 0, integer = 11) + // TODO: including country + // @Digits(message = "phone length error", fraction = 0, integer = 11) + @Column(unique = true) private String mobile; @Builder.Default @@ -84,22 +79,14 @@ public class User extends AuditModel { @Builder.Default private String description = BdConstants.DEFAULT_USER_DESCRIPTION; - /** - * - */ @Builder.Default @Column(name = "is_enabled") private boolean enabled = true; - /** - */ @Builder.Default @Column(name = "is_super") private boolean superUser = false; - /** - * - */ @Builder.Default @Column(name = "is_email_verified") private boolean emailVerified = false; @@ -108,19 +95,32 @@ public class User extends AuditModel { @Column(name = "is_mobile_verified") private boolean mobileVerified = false; - /** - * - */ @Builder.Default - @OneToMany(fetch = FetchType.EAGER) - @JsonManagedReference // 避免无限递归 - private List roles = new ArrayList<>(); + @ManyToMany(fetch = FetchType.EAGER) + private Set roles = new HashSet<>(); - /** - * - */ @Builder.Default - @Convert(converter = StringListConverter.class) - private List organizations = new ArrayList<>(); + @Convert(converter = StringSetConverter.class) + private Set organizations = new HashSet<>(); + // return the first organization oid + public String getOrgOid() { + return this.organizations.isEmpty() ? null : this.organizations.iterator().next(); + } + + /** */ + public void addRole(Role role) { + if (!this.roles.contains(role)) { + this.roles.add(role); + // role.getUsers().add(this); + } + } + + public void removeRole(Role role) { + if (this.roles.contains(role)) { + this.roles.remove(role); + // role.getUsers().remove(this); + } + } + } \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserAspect.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserAspect.java new file mode 100644 index 0000000000..f313212661 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserAspect.java @@ -0,0 +1,49 @@ +/* + * @Visitoror: jackning 270580156@qq.com + * @Date: 2024-04-05 14:51:45 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 12:21:15 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.rbac.user; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Aspect +@Component +public class UserAspect { + + @Pointcut("execution(* com.bytedesk.core.rbac.user.UserService.save(..))") + public void userSave() {} + + @Before("userSave()") + public void beforeUserSave(JoinPoint joinPoint) { + //类名 + String clazzName = joinPoint.getTarget().getClass().getName(); + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + //方法名 + String methodName = methodSignature.getName(); + //参数名数组 + String[] parameters = methodSignature.getParameterNames(); + //参数值: FIXME: user/role toString 循环引用栈溢出 + // Object[] args = joinPoint.getArgs(); + log.info("beforeUserSave {}, {}, {}, {}", clazzName, methodName, parameters); + + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserController.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserController.java index a840cf5577..0d5d5e326c 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserController.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 11:01:20 + * @LastEditTime: 2024-04-25 22:46:49 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,11 +14,11 @@ */ package com.bytedesk.core.rbac.user; -import org.modelmapper.ModelMapper; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import com.bytedesk.core.auth.AuthService; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.utils.JsonResult; import lombok.AllArgsConstructor; @@ -35,8 +35,6 @@ public class UserController { private final AuthService authService; - private final ModelMapper modelMapper; - /** * get user info * @@ -44,16 +42,12 @@ public class UserController { */ @GetMapping("/profile") public ResponseEntity getProfile() { - try { - // - User user = authService.getCurrentUser(); + // + User user = authService.getCurrentUser(); - UserResponse userResponse = modelMapper.map(user, UserResponse.class); + UserResponse userResponse = userService.convertToUserResponse(user); - return ResponseEntity.ok(JsonResult.success(userResponse)); - } catch (Exception e) { - throw new RuntimeException(e); - } + return ResponseEntity.ok(JsonResult.success(userResponse)); } /** @@ -64,56 +58,35 @@ public class UserController { */ @PostMapping("/update") public ResponseEntity update(@RequestBody UserRequest userRequest) { - try { - User user = authService.getCurrentUser(); + UserResponse userResponse = userService.update(userRequest); - UserResponse userResponse = userService.update(userRequest, user); - - return ResponseEntity.ok().body(new JsonResult<>("update user success", 200, userResponse)); - - } catch (Exception e) { - throw new RuntimeException(e); - } + return ResponseEntity.ok(JsonResult.success(userResponse)); } - /** - * - * @return - */ + /** */ @PostMapping("/logout") public ResponseEntity logout() { - try { - return ResponseEntity.ok().body("logout"); - } catch (Exception e) { - throw new RuntimeException(e); - } + // TODO: 清理token,使其过期 + return ResponseEntity.ok().body("logout"); + } + + /** for testing,client will return 403, if dont have authority/role */ + @PreAuthorize("hasAnyAuthority('ROLE_SUPER', 'ROLE_ADMIN')") + // @PreAuthorize("hasAuthority('ROLE_SUPER') or hasAuthority('ROLE_ADMIN')") + // @PreAuthorize("hasAuthority('ROLE_SUPER')") + @GetMapping("/test/super") + public ResponseEntity testSuperAuthority() { + return ResponseEntity.ok("you have super authority"); } - // @PreAuthorize("hasAuthority('ROLE_ADMIN')") - // @GetMapping("/test") - // public String test() { - // try { - // return "Welcome"; - // } catch (Exception e) { - // throw new RuntimeException(e); - // } - // } - - // @GetMapping("/me") - // @PreAuthorize("isAuthenticated()") - // public ResponseEntity authenticatedUser() { - // Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - // User currentUser = (User) authentication.getPrincipal(); - // return ResponseEntity.ok(currentUser); - // } - - // @GetMapping - // @PreAuthorize("hasAnyRole('ADMIN', 'SUPER_ADMIN')") - // public ResponseEntity> allUsers() { - // // List users = userService.allUsers(); - // return ResponseEntity.ok(null); - // } + /** no need to add ROLE_ prefix, system will auto add */ + @GetMapping(value = "/test/cs") + @PreAuthorize("hasAnyRole('ADMIN', 'SUPER', 'CS')") + // @PreAuthorize("hasRole('CS')") + public ResponseEntity testCsRole() { + return ResponseEntity.ok("you have admin or cs role"); + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsImpl.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsImpl.java index acebd6a36f..bdb4d0ecb1 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsImpl.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsImpl.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-23 07:53:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-06 14:34:53 + * @LastEditTime: 2024-04-24 10:13:18 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -18,13 +18,14 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; +import com.bytedesk.core.constant.TypeConsts; import com.bytedesk.core.rbac.role.Role; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import java.util.Collection; -import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Data @@ -40,14 +41,16 @@ public class UserDetailsImpl implements UserDetails { private String description; @JsonIgnore private String password; - private List roles; - private List organizations; + private Set roles; + private Set organizations; + // + private boolean enabled; Collection authorities; - public UserDetailsImpl(Long id, String uid, String username, String nickname, String avatar, String mobile, + private UserDetailsImpl(Long id, String uid, String username, String nickname, String avatar, String mobile, String email, String password, - Collection authorities, List roles, List organizations, - String description) { + Collection authorities, Set roles, Set organizations, + String description, boolean enabled) { this.id = id; this.uid = uid; this.username = username; @@ -60,13 +63,19 @@ public class UserDetailsImpl implements UserDetails { this.roles = roles; this.organizations = organizations; this.description = description; + this.enabled = enabled; } // public static UserDetailsImpl build(User user) { - List authorities = user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority(role.getValue())) - .collect(Collectors.toList()); + // + // Set authorities = user.getRoles().stream() + // .map(role -> new SimpleGrantedAuthority(role.getValue())) + // .collect(Collectors.toSet()); + Set authorities = user.getRoles().stream() + .flatMap(role -> role.getAuthorities().stream() + .map(authority -> new SimpleGrantedAuthority(TypeConsts.ROLE_ + authority.getValue()))) + .collect(Collectors.toSet()); return new UserDetailsImpl(user.getId(), user.getUid(), @@ -79,7 +88,8 @@ public class UserDetailsImpl implements UserDetails { authorities, user.getRoles(), user.getOrganizations(), - user.getDescription()); + user.getDescription(), + user.isEnabled()); } @Override @@ -99,21 +109,25 @@ public class UserDetailsImpl implements UserDetails { @Override public boolean isAccountNonExpired() { - return true; + // return true; + return enabled; } @Override public boolean isAccountNonLocked() { - return true; + // return true; + return enabled; } @Override public boolean isCredentialsNonExpired() { - return true; + // return true; + return enabled; } @Override public boolean isEnabled() { - return true; + // return true; + return enabled; } } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsServiceImpl.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsServiceImpl.java index 7e0b773eb5..9e1dd076d6 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsServiceImpl.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserDetailsServiceImpl.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-23 07:53:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 22:14:36 + * @LastEditTime: 2024-04-26 11:34:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -22,9 +22,9 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import com.bytedesk.core.utils.exception.EmailNotFoundException; -import com.bytedesk.core.utils.exception.ForbiddenException; -import com.bytedesk.core.utils.exception.MobileNotFoundException; +import com.bytedesk.core.exception.EmailNotFoundException; +import com.bytedesk.core.exception.MobileNotFoundException; +import com.bytedesk.core.exception.UserDisabledException; import lombok.extern.slf4j.Slf4j; @@ -47,7 +47,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { throw new UsernameNotFoundException("username " + username + " is not found"); } if (!userOptional.get().isEnabled()) { - throw new ForbiddenException("username " + username + " is not enabled"); + throw new UserDisabledException("username " + username + " is not enabled"); } return UserDetailsImpl.build(userOptional.get()); } @@ -60,7 +60,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { throw new EmailNotFoundException("email " + email + " is not found"); } if (!userOptional.get().isEnabled()) { - throw new ForbiddenException("email " + email + " is not enabled"); + throw new UserDisabledException("email " + email + " is not enabled"); } return UserDetailsImpl.build(userOptional.get()); } @@ -73,7 +73,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { throw new MobileNotFoundException("mobile " + mobile + " is not found"); } if (!userOptional.get().isEnabled()) { - throw new ForbiddenException("mobile " + mobile + " is not enabled"); + throw new UserDisabledException("mobile " + mobile + " is not enabled"); } return UserDetailsImpl.build(userOptional.get()); } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserListener.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserListener.java new file mode 100644 index 0000000000..f5eab557c6 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserListener.java @@ -0,0 +1,63 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 09:30:09 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 09:00:16 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.rbac.user; + +import org.springframework.stereotype.Component; + +import com.bytedesk.core.topic.TopicService; +import com.bytedesk.core.utils.ApplicationContextHolder; + +import jakarta.persistence.PostPersist; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class UserListener { + + // 无法注入bean,否则报错 + // private static TopicService topicService; + + @PostPersist + public void postPersist(User user) { + log.info("user postPersist {}", user.getUid()); + // 这里可以记录日志、发送通知等 + // create user topic + TopicService topicService = ApplicationContextHolder.getBean(TopicService.class); + // + topicService.create(user.getUid(), user.getUid()); + } + + // @PreUpdate + // public void preUpdate(User user) { + // log.info("preUpdate {}", user.getUid()); + // } + + // @PostUpdate + // public void postUpdate(User user) { + // log.info("postUpdate {}", user.getUid()); + // } + + // @PreRemove + // public void preRemove(User user) { + // log.info("preRemove {}", user.getUid()); + // } + + // @PostRemove + // public void postRemove(User user) { + // log.info("postRemove {}", user.getUid()); + // // topicService.deleteByTopicAndUid(user.getUid(), user.getUid()); + // } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRepository.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRepository.java index e6c28e1369..0dac7f688b 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRepository.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 22:11:38 + * @LastEditTime: 2024-04-26 21:10:25 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -32,19 +32,14 @@ import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository { - // @Cacheable(cacheNames = "users") Optional findByEmail(String email); - // @Cacheable(cacheNames = "users") Optional findByMobile(String mobile); - // @Cacheable(cacheNames = "users") Optional findByUsername(String username); - // @Cacheable(cacheNames = "users") Optional findByUid(String uid); - // Boolean existsByUsernameAndDeleted(String username, Boolean deleted); Boolean existsByMobileAndDeleted(String mobile, Boolean deleted); diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRequest.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRequest.java index c134b2463f..5e301404df 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-01 15:58:57 + * @LastEditTime: 2024-04-26 10:53:19 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,14 +14,22 @@ */ package com.bytedesk.core.rbac.user; +import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.utils.BaseRequest; -import jakarta.validation.constraints.Digits; +// import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.Email; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; @Data +@Builder @EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +@AllArgsConstructor public class UserRequest extends BaseRequest { private Long id; @@ -39,17 +47,23 @@ public class UserRequest extends BaseRequest { @Email(message = "email format error") private String email; - @Digits(message = "phone length error", fraction = 0, integer = 11) + // country prefix, e.g. +86 + // @Digits(message = "phone length error", fraction = 0, integer = 11) private String mobile; + private String code; - private String avatar; + @Builder.Default + private String avatar = AvatarConsts.DEFAULT_AVATAR_URL; - private String description; + @Builder.Default + private String description = BdConstants.DEFAULT_USER_DESCRIPTION; - private boolean enabled; + private Boolean enabled; + + // private boolean superUser; - private boolean superUser; + private Boolean emailVerified; - private boolean verified; + private Boolean mobileVerified; } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponse.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponse.java index 59032d4368..113362f951 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 14:46:03 + * @LastEditTime: 2024-04-25 22:52:53 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,10 +21,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -// import java.util.Collection; -import java.util.List; - -// import org.springframework.security.core.GrantedAuthority; +import java.util.HashSet; +import java.util.Set; import com.bytedesk.core.rbac.role.RoleResponse; import com.bytedesk.core.utils.BaseResponse; @@ -37,6 +35,8 @@ import com.bytedesk.core.utils.BaseResponse; @EqualsAndHashCode(callSuper = true) public class UserResponse extends BaseResponse { + private static final long serialVersionUID = -2015147462L; + private String uid; private String username; private String nickname; @@ -44,7 +44,15 @@ public class UserResponse extends BaseResponse { private String mobile; private String avatar; private String description; + // + private Boolean enabled; + private boolean superUser; + private Boolean emailVerified; + private Boolean mobileVerified; // - private List roles; - private List organizations; + @Builder.Default + private Set roles = new HashSet<>(); + + @Builder.Default + private Set organizations = new HashSet<>(); } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponseSimple.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponseSimple.java index 3eb905ed63..636b4f8e38 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponseSimple.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserResponseSimple.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 14:46:40 + * @LastEditTime: 2024-04-16 17:04:38 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -31,11 +31,9 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) public class UserResponseSimple extends BaseResponse { + private static final long serialVersionUID = 4684010695L; + private String uid; private String nickname; - private String email; - private String mobile; private String avatar; - private String description; - } diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserService.java b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserService.java index 1c5784e216..9b725406f0 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserService.java +++ b/modules/core/src/main/java/com/bytedesk/core/rbac/user/UserService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:32:26 + * @LastEditTime: 2024-04-27 12:24:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,34 +15,40 @@ package com.bytedesk.core.rbac.user; import java.util.Optional; +import java.util.Set; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; -// import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.lang.NonNull; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -// import com.bytedesk.core.auth.AuthService; import com.bytedesk.core.config.BytedeskProperties; import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.exception.EmailExistsException; +import com.bytedesk.core.exception.MobileExistsException; +import com.bytedesk.core.exception.NotFoundException; import com.bytedesk.core.rbac.role.Role; import com.bytedesk.core.rbac.role.RoleService; -import com.bytedesk.core.utils.Utils; +import com.bytedesk.core.uid.UidUtils; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; -@Slf4j +// @Slf4j @Service @AllArgsConstructor public class UserService { @@ -60,38 +66,117 @@ public class UserService { private final BCryptPasswordEncoder passwordEncoder; + private final UidUtils uidUtils; + + public Page query(UserRequest userRequest) { + + Pageable pageable = PageRequest.of(userRequest.getPageNumber(), userRequest.getPageSize(), Sort.Direction.ASC, + "updatedAt"); + + return userRepository.findAll(pageable); + } + + @Transactional public UserResponse register(UserRequest userRequest) { - if (userRequest.getUsername() == null) { - throw new RuntimeException("Parameter username is not found in request..!!"); - } else if (userRequest.getPassword() == null) { - throw new RuntimeException("Parameter password is not found in request..!!"); + + if (existsByEmail(userRequest.getEmail())) { + throw new EmailExistsException("Email already exists..!!"); + } + if (existsByMobile(userRequest.getMobile())) { + throw new MobileExistsException("Mobile already exists..!!"); } - String rawPassword = userRequest.getPassword(); - String encodedPassword = passwordEncoder.encode(rawPassword); // User user = modelMapper.map(userRequest, User.class); - user.setPassword(encodedPassword); - // - User savedUser = userRepository.save(user); + user.setUid(uidUtils.getCacheSerialUid()); // - return modelMapper.map(savedUser, UserResponse.class); - } - - public UserResponse update(UserRequest userRequest, User user) { - + if (StringUtils.hasLength(userRequest.getNickname())) { + user.setNickname(userRequest.getNickname()); + } else { + user.setNickname(createNickname()); + } + // + if (StringUtils.hasLength(userRequest.getAvatar())) { + user.setAvatar(userRequest.getAvatar()); + } else { + user.setAvatar(AvatarConsts.DEFAULT_AVATAR_URL); + } + // + if (StringUtils.hasLength(userRequest.getPassword())) { + String rawPassword = userRequest.getPassword(); + String encodedPassword = passwordEncoder.encode(rawPassword); + user.setPassword(encodedPassword); + } + // 只有经过验证的邮箱,才真正执行注册 + if (StringUtils.hasLength(userRequest.getEmail())) { + user.setUsername(userRequest.getEmail()); + user.setNum(userRequest.getEmail()); + user.setEmailVerified(true); + user.setEnabled(true); + } + // 只有经过验证的手机号,才真正执行注册 + if (StringUtils.hasLength(userRequest.getMobile())) { + user.setNum(userRequest.getMobile()); + user.setMobileVerified(true); + user.setEnabled(true); + } + // TODO: 设置角色role + // - - return null; + // + return convertToUserResponse(save(user)); } - // + @Transactional + public UserResponse update(UserRequest userRequest) { + + Optional userOptional = findByUid(userRequest.getUid()); + if (userOptional.isPresent()) { + User user = userOptional.get(); + + if (StringUtils.hasLength(userRequest.getNickname())) { + user.setNickname(userRequest.getNickname()); + } + + if (StringUtils.hasLength(userRequest.getAvatar())) { + user.setAvatar(userRequest.getAvatar()); + } + + if (StringUtils.hasLength(userRequest.getPassword())) { + String rawPassword = userRequest.getPassword(); + String encodedPassword = passwordEncoder.encode(rawPassword); + user.setPassword(encodedPassword); + } + + if (StringUtils.hasLength(userRequest.getEmail())) { + user.setEmail(userRequest.getEmail()); + } + + if (StringUtils.hasLength(userRequest.getMobile())) { + user.setMobile(userRequest.getMobile()); + } + + if(StringUtils.hasLength(userRequest.getDescription())) { + user.setDescription(userRequest.getDescription()); + } + + // TODO: 设置角色role + + return convertToUserResponse(save(user)); + + } else { + throw new NotFoundException("User not found..!!"); + } + } + + @Transactional public User createUser(String nickname, String avatar, String password, String mobile, String email, boolean isVerified, - String organization_oid) { + String orgOid) { User user = User.builder() - .uid(Utils.getUid()) + .uid(uidUtils.getCacheSerialUid()) .avatar(avatar) - .username(mobile) + // use email as default username + .username(email) .nickname(nickname) .mobile(mobile) .num(mobile) @@ -107,10 +192,10 @@ public class UserService { user.setPassword(passwordEncoder.encode("123456")); } - user.getOrganizations().add(organization_oid); + user.getOrganizations().add(orgOid); - // return save(user); - return user; + return save(user); + // return user; } public User updateUser(User user, String password, String mobile, String email) { @@ -126,79 +211,100 @@ public class UserService { return save(user); } - // - @Cacheable(cacheNames = "users", unless="#result == null") + @Cacheable(value = "user", key = "#email", unless="#result == null") public Optional findByEmail(String email) { return userRepository.findByEmail(email); } - @Cacheable(cacheNames = "users", unless="#result == null") + @Cacheable(value = "user", key = "#mobile", unless="#result == null") public Optional findByMobile(String mobile) { return userRepository.findByMobile(mobile); } - @Cacheable(cacheNames = "users", unless="#result == null") + @Cacheable(value = "user", key = "#username", unless="#result == null") public Optional findByUsername(String username) { return userRepository.findByUsername(username); } - @Cacheable(cacheNames = "users", unless="#result == null") + @Cacheable(value = "user", key = "#uid", unless="#result == null") public Optional findByUid(String uid) { return userRepository.findByUid(uid); } - @Cacheable(cacheNames = "admin", unless="#result == null") + @Cacheable(value = "admin", unless="#result == null") public Optional getAdmin() { return userRepository.findByUsername(properties.getUsername()); } // + @Cacheable(value = "userExists", key = "#username", unless="#result == null") public Boolean existsByUsername(String username) { return userRepository.existsByUsernameAndDeleted(username, false); } + @Cacheable(value = "userExists", key = "#mobile", unless="#result == null") public Boolean existsByMobile(String mobile) { return userRepository.existsByMobileAndDeleted(mobile, false); } + @Cacheable(value = "userExists", key = "#email", unless="#result == null") public Boolean existsByEmail(String email) { return userRepository.existsByEmailAndDeleted(email, false); } - @Caching(put = { - @CachePut(cacheNames = "users", key = "#user.username"), - @CachePut(cacheNames = "users", key = "#user.mobile"), - @CachePut(cacheNames = "users", key = "#user.email"), - @CachePut(cacheNames = "users", key = "#user.uid") + @CachePut(value = "user", key = "#user.username"), + @CachePut(value = "user", key = "#user.mobile"), + @CachePut(value = "user", key = "#user.email"), + @CachePut(value = "user", key = "#user.uid"), + // TODO: 此处put的exists内容跟缓存时内容类型是否一致? + // @CachePut(value = "userExists", key = "#user.username"), + // @CachePut(value = "userExists", key = "#user.mobile"), + // @CachePut(value = "userExists", key = "#user.email"), }) public User save(@NonNull User user) { return userRepository.save(user); } @Caching(evict = { - @CacheEvict(cacheNames = "users", key = "#user.username"), - @CacheEvict(cacheNames = "users", key = "#user.mobile"), - @CacheEvict(cacheNames = "users", key = "#user.email"), - @CacheEvict(cacheNames = "users", key = "#user.uid") + @CacheEvict(value = "user", key = "#user.username"), + @CacheEvict(value = "user", key = "#user.mobile"), + @CacheEvict(value = "user", key = "#user.email"), + @CacheEvict(value = "user", key = "#user.uid"), + @CacheEvict(value = "userExists", key = "#user.username"), + @CacheEvict(value = "userExists", key = "#user.mobile"), + @CacheEvict(value = "userExists", key = "#user.email"), }) public void delete(@NonNull User user) { userRepository.delete(user); } + public UserResponse convertToUserResponse(User user) { + return modelMapper.map(user, UserResponse.class); + } + + public UserResponseSimple convertToUserResponseSimple(User user) { + return modelMapper.map(user, UserResponseSimple.class); + } + + // TODO: 待完善 + public String createNickname() { + String randomId = uidUtils.getCacheSerialUid().substring(11, 15); + return "User" + randomId; + } + public void initData() { if (userRepository.count() > 0) { - log.debug("user already exists"); return; } - // + User admin = User.builder() - .uid(Utils.getUid()) + .uid(uidUtils.getCacheSerialUid()) .email(properties.getEmail()) .username(properties.getUsername()) .password(new BCryptPasswordEncoder().encode(properties.getPassword())) - .nickname("Admin") + .nickname(properties.getNickname()) .avatar(AvatarConsts.DEFAULT_AVATAR_URL) .mobile(properties.getMobile()) .num(properties.getMobile()) @@ -207,14 +313,13 @@ public class UserService { .mobileVerified(true) .build(); // - Optional roleOptional = roleService.findByValue(TypeConsts.ROLE_ADMIN); - List roles = new ArrayList<>(); - if (roleOptional.isPresent()) { - roles.add(roleOptional.get()); - } - // + Optional roleOptional = roleService.findByName(TypeConsts.ROLE_SUPER); + Set roles = new HashSet<>(); + roleOptional.ifPresent(role -> { + roles.add(role); + }); admin.setRoles(roles); - userRepository.save(admin); + save(admin); } } diff --git a/modules/core/src/main/java/com/bytedesk/core/redis/RedisLoginService.java b/modules/core/src/main/java/com/bytedesk/core/redis/RedisLoginService.java deleted file mode 100644 index c6be33aa64..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/redis/RedisLoginService.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.bytedesk.core.redis; - -import lombok.AllArgsConstructor; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import com.bytedesk.core.constant.RedisConsts; - -import java.util.concurrent.TimeUnit; - -// @Slf4j -@Service -@AllArgsConstructor -public class RedisLoginService { - - private final StringRedisTemplate stringRedisTemplate; - - /** - * cache validate code for 15min - * 缓存验证码, 缓存时间15分钟 - * - * @param keyNum email/mobile - * @param code code - */ - public void cacheValidateCode(String keyNum, @NonNull String code) { - String key = RedisConsts.VALIDATE_CODE + keyNum; - stringRedisTemplate.opsForValue().set(key, code, 60 * 15, TimeUnit.SECONDS); - } - - /** - * validate code - * 验证验证码是否正确 - * - * @param keyNum email/mobile - * @param code code - * @return boolean - */ - public boolean validateCode(String keyNum, String code) { - String key = RedisConsts.VALIDATE_CODE + keyNum; - String cacheCode = stringRedisTemplate.opsForValue().get(key); - if (code.equals(cacheCode)) { - return true; - } - return false; - } - - /** - * check validate code - * 是否缓存有验证码 - * - * @param keyNum - * @return - */ - public boolean hasValidateCode(String keyNum) { - String key = RedisConsts.VALIDATE_CODE + keyNum; - String cacheCode = stringRedisTemplate.opsForValue().get(key); - if (!StringUtils.hasLength(cacheCode)) { - return false; - } - return true; - } - - /** - * cache ip for 60min - * 缓存注册ip, 缓存时间60分钟 - * 不允许同一个ip短时间内重复注册 - * - * @param ip ip - */ - public void cacheRegisterIP(@NonNull String ip) { - String key = RedisConsts.REGISTE_IP + ip; - stringRedisTemplate.opsForValue().set(key, ip, 60 * 60, TimeUnit.SECONDS); - } - - /** - * check ip - * 是否缓存有ip - * - * @param ip - * @return - */ - public boolean hasRegisterIP(String ip) { - String key = RedisConsts.REGISTE_IP + ip; - String cacheIp = stringRedisTemplate.opsForValue().get(key); - if (!StringUtils.hasLength(cacheIp)) { - return false; - } - return true; - } - - /** - * cache visitor ip - * 限制访客端 - * 缓存注册ip, 缓存时间5分钟 - * 不允许同一个ip短时间内重复注册 - * - * @param ip ip - */ - public void cacheVisitorRegisterIP(@NonNull String ip) { - String key = RedisConsts.REGISTE_IP_VISITOR + ip; - stringRedisTemplate.opsForValue().set(key, ip, 60 * 5, TimeUnit.SECONDS); - } - - /** - * check visitor ip - * 限制访客端 - * 是否缓存有ip - * - * @param ip - * @return - */ - public boolean hasVisitorRegisterIP(String ip) { - String key = RedisConsts.REGISTE_IP_VISITOR + ip; - String cacheIp = stringRedisTemplate.opsForValue().get(key); - if (!StringUtils.hasLength(cacheIp)) { - return false; - } - return true; - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/redis/RedisUserService.java b/modules/core/src/main/java/com/bytedesk/core/redis/RedisUserService.java deleted file mode 100644 index a2221ab7c7..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/redis/RedisUserService.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-27 10:54:35 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 13:20:36 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.redis; - -import java.util.Set; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import com.bytedesk.core.constant.RedisConsts; -import lombok.AllArgsConstructor; - -@Service -@AllArgsConstructor -public class RedisUserService { - - private final StringRedisTemplate stringRedisTemplate; - - // private final RedisThreadService redisThreadService; - - public void addAccessToken(String username, String accessToken) { - String key = RedisConsts.ACCESS_TOKEN_PREFIX + username; - stringRedisTemplate.opsForSet().add(key, accessToken); - } - - /** - * 存储客服订阅的topic - */ - - // 判断用户是否被缓存 - @SuppressWarnings("null") - public boolean hasTopics(@NonNull String uid) { - return stringRedisTemplate.opsForSet().isMember(RedisConsts.USER_TOPIC_PREFIX + uid, uid); - } - - // 存储用户topic - public void addTopic(@NonNull String uid, @NonNull String topic) { - stringRedisTemplate.opsForSet().add(RedisConsts.USER_TOPIC_PREFIX + uid, topic); - } - - public void removeTopic(@NonNull String uid, @NonNull String topic) { - stringRedisTemplate.opsForSet().remove(RedisConsts.USER_TOPIC_PREFIX + uid, topic); - } - - public Set getTopics(@NonNull String uid) { - return stringRedisTemplate.opsForSet().members(RedisConsts.USER_TOPIC_PREFIX + uid); - } - - // 判断客服账号是否被禁用 - // private static final String USER_DISABLED = "bytedeskim:user:disabled"; // - // "bytedeskim:user:enabled"; - - public void enable(@NonNull String uid) { - stringRedisTemplate.opsForSet().remove(RedisConsts.USER_DISABLED, uid); - } - - public void disable(@NonNull String uid) { - stringRedisTemplate.opsForSet().add(RedisConsts.USER_DISABLED, uid); - } - - // 判断用户是否被禁用 - @SuppressWarnings("null") - public boolean isDisabled(@NonNull String uid) { - return stringRedisTemplate.opsForSet().isMember(RedisConsts.USER_DISABLED, uid); - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/session/HttpSessionConfig.java b/modules/core/src/main/java/com/bytedesk/core/session/HttpSessionConfig.java index 6b163bc75c..0fde925403 100644 --- a/modules/core/src/main/java/com/bytedesk/core/session/HttpSessionConfig.java +++ b/modules/core/src/main/java/com/bytedesk/core/session/HttpSessionConfig.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-05 00:04:38 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 10:40:10 + * @LastEditTime: 2024-04-13 14:25:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,42 +12,49 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.session; +// package com.bytedesk.core.session; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; +// import org.springframework.context.annotation.Bean; +// import org.springframework.context.annotation.Configuration; +// import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +// import org.springframework.data.redis.serializer.RedisSerializer; +// import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; -/** - * https://docs.spring.io/spring-session/reference/guides/boot-redis.html - * - * @description - * @author jackning - * @date 2024-04-05 00:15:55 - */ -@Configuration -@EnableRedisHttpSession(redisNamespace = "bytedeskim") -public class HttpSessionConfig { +// /** +// * https://docs.spring.io/spring-session/reference/guides/boot-redis.html +// * +// * @description +// * @author jackning +// * @date 2024-04-05 00:15:55 +// */ +// @Configuration +// @EnableRedisHttpSession(redisNamespace = "bytedeskim") +// public class HttpSessionConfig { - @Bean - public RedisSerializer springSessionDefaultRedisSerializer() { - return new GenericJackson2JsonRedisSerializer(); - } +// @Bean +// public RedisSerializer springSessionDefaultRedisSerializer() { +// return new GenericJackson2JsonRedisSerializer(); +// } - /** - * Custom Cookie - * https://docs.spring.io/spring-session/reference/guides/java-custom-cookie.html#custom-cookie-sample - * @return - */ - // @Bean - // public CookieSerializer cookieSerializer() { - // DefaultCookieSerializer serializer = new DefaultCookieSerializer(); - // serializer.setCookieName("JSESSIONID"); - // serializer.setCookiePath("/"); - // serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); - // return serializer; - // } +// /** +// * Custom Cookie +// * https://docs.spring.io/spring-session/reference/guides/java-custom-cookie.html#custom-cookie-sample +// * @return +// */ +// // @Bean +// // public CookieSerializer cookieSerializer() { +// // DefaultCookieSerializer serializer = new DefaultCookieSerializer(); +// // serializer.setCookieName("JSESSIONID"); +// // serializer.setCookiePath("/"); +// // serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); +// // return serializer; +// // } -} +// } + + +// \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/Thread.java b/modules/core/src/main/java/com/bytedesk/core/thread/Thread.java index cbe3fefcab..f00b4f02e9 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/Thread.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/Thread.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-01 15:20:24 + * @LastEditTime: 2024-04-23 09:55:39 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,7 +14,12 @@ */ package com.bytedesk.core.thread; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + import com.bytedesk.core.constant.BdConstants; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.AuditModel; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -27,6 +32,10 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +/** + * every visitor <-> agent thread should only be one, + * history records are stored in thread_log table + */ @Entity @Data @Builder @@ -34,6 +43,7 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor +@EntityListeners({ ThreadListener.class }) @Table(name = "core_thread") public class Thread extends AuditModel { @@ -44,37 +54,57 @@ public class Thread extends AuditModel { @Column(unique = true, nullable = false) private String tid; - private String nickname; - - private String avatar; - + /** + * used to push message + * topic format: + * workgroup_wid + '/' + visitor_vid + * agent_aid + '/' + visitor_vid + * such as: wid/vid or aid/vid + */ private String topic; @Builder.Default - private String content = BdConstants.EMPTY; + private String content = BdConstants.EMPTY_STRING; @Builder.Default private Integer unreadCount = 0; /** - * + * @{ThreadTypeConsts} */ - @Column(name = "by_type") - private String type; - - /** - * - */ - @Lob @Builder.Default - private String extra = BdConstants.EMPTY; + @Column(name = "by_type") + private String type = ThreadTypeConsts.WORKGROUP; + + /** closed/open, agent closed/auto closed */ + @Builder.Default + private String status = StatusConsts.THREAD_STATUS_OPEN; + + private String client; + + // @Lob + @Builder.Default + @Column(columnDefinition = "json") + // 用于兼容postgreSQL,否则会报错,[ERROR: column "extra" is of type json but expression is of type character varying + @JdbcTypeCode(SqlTypes.JSON) + private String extra = BdConstants.EMPTY_JSON_STRING; + + // + // h2 db 不能使用 user, 所以重定义为 by_user + @Builder.Default + @Column(name = "by_user", columnDefinition = "json") + @JdbcTypeCode(SqlTypes.JSON) + private String user = BdConstants.EMPTY_JSON_STRING; /** - * + * belongs to user */ @JsonIgnore @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) - private User user; + private User owner; + + // TODO: + /** belong to org */ + private String orgOid; } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadController.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadController.java index c162f9d487..71c2885d5d 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadController.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 11:30:17 + * @LastEditTime: 2024-04-23 10:07:32 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,7 +15,9 @@ package com.bytedesk.core.thread; import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -41,22 +43,43 @@ public class ThreadController { * @return json */ @GetMapping("/query") - public JsonResult query(BaseRequest pageParam) { + public ResponseEntity query(BaseRequest pageParam) { - Page threadPage = threadService.query(pageParam); + Page threadPage = threadService.query(pageParam); // - return new JsonResult<>("query thread success", 200, threadPage); + return ResponseEntity.ok(JsonResult.success(threadPage)); } - @RequestMapping("/create") - public JsonResult create(@RequestBody ThreadRequest threadRequest) { - - Thread thread = threadService.create(threadRequest); + /** + * user create member thread + * @param threadRequest + * @return + */ + @PostMapping("/create") + public ResponseEntity create(@RequestBody ThreadRequest threadRequest) { + // + ThreadResponse thread = threadService.createMemberThread(threadRequest); if (thread == null) { - return new JsonResult<>("create thread failed", -1, null); + return ResponseEntity.ok(JsonResult.error()); } - return new JsonResult<>("create thread success", 200, thread); + return ResponseEntity.ok(JsonResult.success(thread)); } + + + + // @PostMapping("/delete") + // public ResponseEntity delete(@RequestBody ThreadRequest threadRequest) { + // Thread thread = threadService.findByTid(threadRequest.getTid()).orElse(null); + // if (thread == null) { + // return ResponseEntity.ok(JsonResult.error()); + // } + // threadService.delete(thread); + // return ResponseEntity.ok(JsonResult.success()); + // } + + + + } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadEventHandler.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadEventHandler.java index 36830cc154..754553627e 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadEventHandler.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadEventHandler.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-22 10:40:06 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 11:11:52 + * @LastEditTime: 2024-04-15 09:32:18 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -25,13 +25,11 @@ import org.springframework.data.rest.core.annotation.HandleBeforeSave; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.stereotype.Component; -import com.bytedesk.core.auth.AuthService; -import com.bytedesk.core.utils.Utils; - import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; /** + * used for spring-data-rest api * https://spring.io/guides/tutorials/react-and-spring-data-rest/ * https://docs.spring.io/spring-data/rest/reference/events.html */ @@ -41,14 +39,14 @@ import lombok.extern.slf4j.Slf4j; @RepositoryEventHandler(Thread.class) public class ThreadEventHandler { - private AuthService authService; + // private AuthService authService; @HandleBeforeCreate public void beforeCreate(Thread thread) { log.debug("beforeCreate"); // - thread.setTid(Utils.getUid()); - thread.setUser(authService.getCurrentUser()); + // thread.setTid(Utils.getUid()); + // thread.setOwner(authService.getCurrentUser()); return; } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadListener.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadListener.java new file mode 100644 index 0000000000..adda5ea2bd --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadListener.java @@ -0,0 +1,72 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 09:30:56 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 12:12:59 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.thread; + +import org.springframework.stereotype.Component; + +import com.bytedesk.core.event.BytedeskEventPublisher; +import com.bytedesk.core.topic.TopicService; +import com.bytedesk.core.utils.ApplicationContextHolder; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostUpdate; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class ThreadListener { + + // @PrePersist + // public void prePersist(Thread thread) { + // log.info("prePersist {}", thread.getTid()); + // } + + @PostPersist + public void postPersist(Thread thread) { + log.info("thread postPersist {}", thread.getTid()); + // 这里可以记录日志 + // create thread topic + TopicService topicService = ApplicationContextHolder.getBean(TopicService.class); + topicService.create(thread.getTopic(), thread.getOwner().getUid()); + // send notifications + BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class); + bytedeskEventPublisher.publishThreadCreateEvent(thread); + } + + // @PreUpdate + // public void preUpdate(Thread thread) { + // log.info("preUpdate {}", thread.getTid()); + // } + + @PostUpdate + public void postUpdate(Thread thread) { + log.info("postUpdate {}", thread.getTid()); + // + BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class); + bytedeskEventPublisher.publishThreadUpdateEvent(thread); + } + + // @PreRemove + // public void preRemove(Thread thread) { + // log.info("preRemove {}", thread.getTid()); + // } + + // @PostRemove + // public void postRemove(Thread thread) { + // log.info("postRemove {}", thread.getTid()); + // // topicService.deleteByTopicAndUid(thread.getTopic(), thread.getOwner().getUid()); + // } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRepository.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRepository.java index 120d714c05..be76f3af97 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRepository.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 15:04:11 + * @LastEditTime: 2024-04-22 22:30:34 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,12 +14,14 @@ */ package com.bytedesk.core.thread; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +// import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import com.bytedesk.core.rbac.user.User; @@ -31,12 +33,23 @@ import io.swagger.v3.oas.annotations.tags.Tag; */ @Repository @Tag(name = "thread - 会话") -public interface ThreadRepository - extends JpaRepository, JpaSpecificationExecutor { - +public interface ThreadRepository extends JpaRepository, JpaSpecificationExecutor { Optional findByTid(String tid); + /** used for member thread type */ + Optional findFirstByTopicAndOwner(String topic, User owner); - Optional findByTopicAndUser(String topic, User user); + Optional findFirstByTopic(String topic); - Page findByUser(User user, Pageable pageable); + Page findByOwner(User owner, Pageable pageable); + + // @Query(value="select * from core_thread where extra like %?1% ", nativeQuery = true) + // public Boolean existByExtra(String vid); + + // FIXME: h2不兼容 JSON_EXTRACT + // FIXME: PostgreSQL ERROR: function json_extract(json, unknown) does not exist + // @Query(value = "SELECT * FROM core_thread WHERE JSON_EXTRACT(extra, '$.closed') = false", nativeQuery = true) + // List findByExtraClosed(); + List findByStatus(String status); + + // List findByUpdatedAtAfter(Date updatedAt); } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRequest.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRequest.java index 2ba609795d..2f922c6dfc 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-21 10:01:12 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-01 15:21:22 + * @LastEditTime: 2024-04-13 16:25:54 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,22 +14,27 @@ */ package com.bytedesk.core.thread; +import com.bytedesk.core.rbac.user.UserResponseSimple; import com.bytedesk.core.utils.BaseRequest; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; @Data -@EqualsAndHashCode(callSuper=false) +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +@AllArgsConstructor public class ThreadRequest extends BaseRequest { private String tid; private String topic; - private String nickname; - - private String avatar; - - private String extra; + private UserResponseSimple user; } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponse.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponse.java index b3a60c81fb..0359cec0cc 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-21 10:01:27 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-21 10:01:33 + * @LastEditTime: 2024-04-18 12:38:40 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,9 +14,43 @@ */ package com.bytedesk.core.thread; +import java.util.Date; + +import com.bytedesk.core.rbac.user.UserResponseSimple; +import com.bytedesk.core.utils.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +/** + * for agent client + */ @Data -public class ThreadResponse { +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class ThreadResponse extends BaseResponse { + private static final long serialVersionUID = -9969282117L; + + private String tid; + + private String topic; + + private String content; + + private Integer unreadCount; + + private String type; + + private String extra; + + private Date createdAt; + + private UserResponseSimple user; } diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponseSimple.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponseSimple.java new file mode 100644 index 0000000000..18bc666d12 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadResponseSimple.java @@ -0,0 +1,44 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-02-21 10:01:27 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 17:04:09 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.thread; + +import com.bytedesk.core.utils.BaseResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * for visitor client + */ +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class ThreadResponseSimple extends BaseResponse { + + private static final long serialVersionUID = -2417833247L; + + private String tid; + + private String topic; + + private String type; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadService.java b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadService.java index 70e1e2cb93..38b0aa6132 100644 --- a/modules/core/src/main/java/com/bytedesk/core/thread/ThreadService.java +++ b/modules/core/src/main/java/com/bytedesk/core/thread/ThreadService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-11 15:03:14 + * @LastEditTime: 2024-04-27 12:29:48 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,26 +14,34 @@ */ package com.bytedesk.core.thread; +import java.util.List; import java.util.Optional; import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import com.bytedesk.core.auth.AuthService; +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.User; +import com.bytedesk.core.rbac.user.UserResponseSimple; import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.BaseRequest; -import com.bytedesk.core.utils.Utils; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; -// @Slf4j +@Slf4j @Service @AllArgsConstructor public class ThreadService { @@ -46,7 +54,10 @@ public class ThreadService { private ThreadRepository threadRepository; - public Page query(BaseRequest pageParam) { + private UidUtils uidUtils; + + /** */ + public Page query(BaseRequest pageParam) { User user = authService.getCurrentUser(); @@ -54,30 +65,43 @@ public class ThreadService { Pageable pageable = PageRequest.of(pageParam.getPageNumber(), pageParam.getPageSize(), Sort.Direction.DESC, "updatedAt"); - return threadRepository.findByUser(user, pageable); + Page threadPage = findByOwner(user, pageable); + + return threadPage.map(this::convertToThreadResponse); } - public Thread create(ThreadRequest threadRequest) { + /** + * create member thread + * + * @param threadRequest + * @return + */ + public ThreadResponse createMemberThread(ThreadRequest threadRequest) { - User user = authService.getCurrentUser(); - - Optional threadOptional = threadRepository.findByTopicAndUser(threadRequest.getTopic(), user); + User owner = authService.getCurrentUser(); + // + Optional threadOptional = findByTopicAndOwner(threadRequest.getTopic(), owner); if (threadOptional.isPresent()) { - return threadOptional.get(); - } - - Thread thread = modelMapper.map(threadRequest, Thread.class); - if (!StringUtils.hasText(thread.getTid())) { - thread.setTid(Utils.getUid()); + return convertToThreadResponse(threadOptional.get()); } // + Thread thread = modelMapper.map(threadRequest, Thread.class); + thread.setTid(uidUtils.getCacheSerialUid()); + thread.setStatus(StatusConsts.THREAD_STATUS_INIT); + // + String user = JSON.toJSONString(threadRequest.getUser()); + log.info("request {}, user {}", threadRequest.toString(), user); thread.setUser(user); - return threadRepository.save(thread); + // + thread.setOwner(owner); + Thread result = save(thread); + // + return convertToThreadResponse(result); } - - @SuppressWarnings("null") + + /** */ public Thread getReverse(Thread thread) { - + String reverseTid = new StringBuffer(thread.getTid()).reverse().toString(); Optional reverseThreadOptional = findByTid(reverseTid); if (reverseThreadOptional.isPresent()) { @@ -90,29 +114,117 @@ public class ThreadService { } Thread reverseThread = new Thread(); reverseThread.setTid(reverseTid); - reverseThread.setTopic(thread.getUser().getUid()); - reverseThread.setAvatar(thread.getUser().getAvatar()); - reverseThread.setNickname(thread.getUser().getNickname()); + reverseThread.setTopic(thread.getOwner().getUid()); + // + UserResponseSimple user = userService.convertToUserResponseSimple(thread.getOwner()); + reverseThread.setUser(JSON.toJSONString(user)); + // reverseThread.setAvatar(thread.getOwner().getAvatar()); + // reverseThread.setNickname(thread.getOwner().getNickname()); + // reverseThread.setContent(thread.getContent()); // reverseThread.setExtra(thread.getExtra()); reverseThread.setType(thread.getType()); - reverseThread.setUser(userOptional.get()); + reverseThread.setOwner(userOptional.get()); // return save(reverseThread); } } + /** */ + public Thread createAsistantThread(ThreadRequest threadRequest) { + + + + return null; + } + + + @Cacheable(value = "thread", key = "#tid", unless = "#result == null") public Optional findByTid(String tid) { return threadRepository.findByTid(tid); } + // TODO: how to cacheput or cacheevict? + @Cacheable(value = "thread", key = "#topic-#user.uid", unless = "#result == null") + public Optional findByTopicAndOwner(String topic, User user) { + return threadRepository.findFirstByTopicAndOwner(topic, user); + } + + @Cacheable(value = "thread", key = "#topic", unless = "#result == null") + public Optional findByTopic(String topic) { + return threadRepository.findFirstByTopic(topic); + } + + // TODO: how to cacheput or cacheevict? + @Cacheable(value = "thread", key = "#user.uid-#pageable.getPageNumber()", unless = "#result == null") + public Page findByOwner(User user, Pageable pageable) { + return threadRepository.findByOwner(user, pageable); + } + + // TODO: 更新缓存 + @Cacheable(value = "threadOpen") + public List findStatusOpen() { + return threadRepository.findByStatus(StatusConsts.THREAD_STATUS_OPEN); + } + + public Boolean isClosed(Thread thread) { + return !StatusConsts.THREAD_STATUS_OPEN.equals(thread.getStatus()); + } + + public Thread reopen(Thread thread) { + thread.setStatus(StatusConsts.THREAD_STATUS_OPEN); + return save(thread); + } + + public Thread autoClose(Thread thread) { + thread.setStatus(StatusConsts.THREAD_STATUS_CLOSED_AUTO); + return save(thread); + } + + public Thread agentClose(Thread thread) { + thread.setStatus(StatusConsts.THREAD_STATUS_CLOSED_AGENT); + return save(thread); + } + + @Caching(put = { + @CachePut(value = "thread", key = "#thread.tid"), + @CachePut(value = "thread", key = "#thread.topic") + }) public Thread save(@NonNull Thread thread) { return threadRepository.save(thread); } + @Caching(evict = { + @CacheEvict(value = "thread", key = "#thread.tid"), + @CacheEvict(value = "thread", key = "#thread.topic") + }) public void delete(@NonNull Thread thread) { threadRepository.delete(thread); } + public ThreadResponse convertToThreadResponse(Thread thread) { + ThreadResponse threadResponse = modelMapper.map(thread, ThreadResponse.class); + // + UserResponseSimple user = JSON.parseObject(thread.getUser(), UserResponseSimple.class); + threadResponse.setUser(user); + + return threadResponse; + } + + public ThreadResponseSimple convertToThreadResponseSimple(Thread thread) { + return modelMapper.map(thread, ThreadResponseSimple.class); + } + + // + public void initData() { + + if(threadRepository.count() > 0) { + return; + } + + + + + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/topic/Topic.java b/modules/core/src/main/java/com/bytedesk/core/topic/Topic.java new file mode 100644 index 0000000000..770bdac7c4 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/topic/Topic.java @@ -0,0 +1,95 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-13 16:03:44 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 16:09:01 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.topic; + +import java.util.Set; +import java.util.HashSet; + +import com.bytedesk.core.utils.AuditModel; +import com.bytedesk.core.utils.StringSetConverter; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Entity +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "core_topic", uniqueConstraints = { + // @UniqueConstraint(columnNames = {"topic", "uuid"}), +}) +public class Topic extends AuditModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(unique = true, nullable = false) + private String tid; + + // @Column(nullable = false) + // private String topic; + /** 为防止后添加的记录,clientIds缺失,所以用数组代替,这样每个用户在topic中只有一条记录,cliendIds可共用 */ + @Builder.Default + @Column(length = 512) + @Convert(converter = StringSetConverter.class) + private Set topics = new HashSet<>(); + // private String topic; + + // user, no need map, just uid + @Column(name = "uuid", nullable = false) + private String uid; + + /** AT_MOST_ONCE(0),AT_LEAST_ONCE(1), EXACTLY_ONCE(2), */ + // @Builder.Default + // private int qos = 1; + + // @Builder.Default + // @Column(name = "is_subscribed") + // private boolean subscribed = false; + + /** + * topic通配符说明: + * 单层通配符"+":只能匹配一层主题。例如,"aaa/+"可以匹配"aaa/bbb",但不能匹配"aaa/bbb/ccc"。 + * 多层通配符"#":可以匹配多层主题。例如,"aaa/#"不但可以匹配"aaa/bbb",还可以匹配"aaa/bbb/ccc/ddd"。它必须作为主题的最后一个级别使用,并且只能出现在最后 + */ + // @Builder.Default + // @Column(name = "is_wildcard") + // private boolean wildcard = false; + + /** + * current online clientIds + */ + @Builder.Default + @Convert(converter = StringSetConverter.class) + private Set clientIds = new HashSet<>(); + + /** 描述 */ + // private String description; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/AndroidService.java b/modules/core/src/main/java/com/bytedesk/core/topic/TopicController.java similarity index 84% rename from modules/core/src/main/java/com/bytedesk/core/push/AndroidService.java rename to modules/core/src/main/java/com/bytedesk/core/topic/TopicController.java index 29d369b708..1494599b77 100644 --- a/modules/core/src/main/java/com/bytedesk/core/push/AndroidService.java +++ b/modules/core/src/main/java/com/bytedesk/core/topic/TopicController.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-31 15:30:30 + * @Date: 2024-04-13 16:14:26 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-31 15:30:57 + * @LastEditTime: 2024-04-13 16:14:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,8 +12,8 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.push; +package com.bytedesk.core.topic; -public class AndroidService { +public class TopicController { } diff --git a/modules/core/src/main/java/com/bytedesk/core/topic/TopicRepository.java b/modules/core/src/main/java/com/bytedesk/core/topic/TopicRepository.java new file mode 100644 index 0000000000..5338158af8 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/topic/TopicRepository.java @@ -0,0 +1,50 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-13 16:14:47 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 16:16:14 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.topic; + +import java.util.Optional; +import java.util.Set; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface TopicRepository extends JpaRepository { + + Optional findByTid(String tid); + + Optional findFirstByUid(String uid); + + // boolean existsByTopicAndUid(String topic, String uid); + + // exact match 精确匹配 + // List findByTopicAndUid(String topic, String uid); + + /** + * TODO: wildcard match 通配符匹配 topic + * topic通配符说明: + * 单层通配符"+":只能匹配一层主题。例如,"aaa/+"可以匹配"aaa/bbb",但不能匹配"aaa/bbb/ccc"。 + * 多层通配符"#":可以匹配多层主题。例如,"aaa/#"不但可以匹配"aaa/bbb",还可以匹配"aaa/bbb/ccc/ddd"。它必须作为主题的最后一个级别使用,并且只能出现在最后 + */ + // List findByTopicStartsWith(String topic); + // List findByTopic(String topic); + // + @Query(value="select * from core_topic where topics like %:topic% ", nativeQuery = true) + Set findByTopicsContains(@Param("topic") String topic); + + // void deleteByTopicAndUid(String topic, String uid); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/topic/TopicRequest.java b/modules/core/src/main/java/com/bytedesk/core/topic/TopicRequest.java new file mode 100644 index 0000000000..05bf3cab08 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/topic/TopicRequest.java @@ -0,0 +1,59 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-13 16:15:11 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 14:23:43 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.topic; + +import java.util.ArrayList; +import java.util.List; + +import com.bytedesk.core.utils.BaseRequest; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +@AllArgsConstructor +public class TopicRequest extends BaseRequest { + + private String tid; + + private String topic; + + private String uid; + + /** AT_MOST_ONCE(0),AT_LEAST_ONCE(1), EXACTLY_ONCE(2), */ + // @Builder.Default + // private int qos = 1; + + // private boolean subscribed; + + // private boolean wildcard; + + /** + * current online clientIds + */ + @Builder.Default + private List clientIds = new ArrayList<>(); + + /** 描述 */ + // private String description; +} diff --git a/modules/core/src/main/java/com/bytedesk/core/topic/TopicResponse.java b/modules/core/src/main/java/com/bytedesk/core/topic/TopicResponse.java new file mode 100644 index 0000000000..34a964dd40 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/topic/TopicResponse.java @@ -0,0 +1,56 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-13 16:15:22 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 09:41:32 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.topic; + +import java.util.ArrayList; +import java.util.List; + +import com.bytedesk.core.utils.BaseResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + + +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TopicResponse extends BaseResponse { + + private String tid; + + private String topic; + + private String uid; + + /** AT_MOST_ONCE(0),AT_LEAST_ONCE(1), EXACTLY_ONCE(2), */ + private int qos; + + // private boolean subscribed; + + private boolean wildcard; + + /** + * current online clientIds + */ + @Builder.Default + private List clientIds = new ArrayList<>(); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/topic/TopicService.java b/modules/core/src/main/java/com/bytedesk/core/topic/TopicService.java new file mode 100644 index 0000000000..5fe4fa455a --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/topic/TopicService.java @@ -0,0 +1,209 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-13 16:14:36 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 12:06:52 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.topic; + +import java.util.Optional; +import java.util.Set; + +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.scheduling.annotation.Async; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.bytedesk.core.uid.UidUtils; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class TopicService { + + private final TopicRepository topicRepository; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + @Async + public void create(String topic, String uid) { + TopicRequest topicRequest = TopicRequest.builder() + .topic(topic) + .uid(uid) + // .qos(1) + .build(); + create(topicRequest); + } + + @Transactional + private void create(TopicRequest topicRequest) { + + // if (existsByTopicAndUid(topicRequest.getTopic(), topicRequest.getUid())) { + // return; + // } + + Optional topicOptional = findByUid(topicRequest.getUid()); + if (topicOptional.isPresent()) { + Topic topicElement = topicOptional.get(); + if (topicElement.getTopics().contains(topicRequest.getTopic())) { + log.info("create: {}", topicRequest.getTopic()); + return; + } + topicElement.getTopics().add(topicRequest.getTopic()); + // + save(topicElement); + // + return; + } + // + topicRequest.setTid(uidUtils.getCacheSerialUid()); + // + Topic topic = modelMapper.map(topicRequest, Topic.class); + topic.getTopics().add(topicRequest.getTopic()); + // Topics topicsObject = new Topics(); + // topicsObject.getTopics().add(topicRequest.getTopic()); + // topic.setTopic(JSON.toJSONString(topicsObject)); + // + save(topic); + } + + + @Async + public void subscribe(String topic, String clientId) { + // 用户clientId格式: uid/client + Optional topicOptional = findByClientId(clientId); + if (topicOptional.isPresent()) { + Topic topicElement = topicOptional.get(); + if (topicElement.getTopics().contains(topic)) { + log.info("create: {}", topic); + return; + } + topicElement.getTopics().add(topic); + // + save(topicElement); + } else { + // create + final String uid = clientId.split("/")[0]; + TopicRequest topicRequest = TopicRequest.builder() + .topic(topic) + .uid(uid) + // .qos(qos) + .build(); + topicRequest.getClientIds().add(clientId); + create(topicRequest); + } + } + + @Async + public void unsubscribe(String topic, String clientId) { + // 用户clientId格式: uid/client + Optional topicOptional = findByClientId(clientId); + if (topicOptional.isPresent()) { + Topic topicElement = topicOptional.get(); + if (topicElement.getTopics().contains(topic)) { + log.info("create: {}", topic); + return; + } + topicElement.getTopics().add(topic); + } + // final String uid = clientId.split("/")[0]; + // deleteByTopicAndUid(topic, uid); + } + + @Async + public void addClientId(String clientId) { + // 用户clientId格式: uid/client + Optional topicOptional = findByClientId(clientId); + if (topicOptional.isPresent()) { + Topic topic = topicOptional.get(); + if (!topic.getClientIds().contains(clientId)) { + log.info("addClientId: {}", clientId); + topic.getClientIds().add(clientId); + save(topic); + } + } + } + + @Async + public void removeClientId(String clientId) { + // 用户clientId格式: uid/client + Optional topicOptional = findByClientId(clientId); + if (topicOptional.isPresent()) { + Topic topic = topicOptional.get(); + if (topic.getClientIds().contains(clientId)) { + log.info("removeClientId: {}", clientId); + topic.getClientIds().remove(clientId); + save(topic); + } + } + } + + @Cacheable(value = "topic", key = "#tid") + public Optional findByTid(String tid) { + return topicRepository.findByTid(tid); + } + + @Cacheable(value = "topic", key = "#clientId", unless = "#result == null") + public Optional findByClientId(String clientId) { + // 用户clientId格式: uid/client + final String uid = clientId.split("/")[0]; + return findByUid(uid); + } + + @Cacheable(value = "topic", key = "#uid", unless = "#result == null") + public Optional findByUid(String uid) { + return topicRepository.findFirstByUid(uid); + } + + @Cacheable(value = "topic", key = "#topic", unless="#result == null") + public Set findByTopic(String topic) { + // List topics = topicRepository.findByTopicStartsWith(topic); + Set topics = topicRepository.findByTopicsContains(topic); + return topics; + // return topics.stream().map(this::convertToTopicResponse).toList(); + } + + @Caching(put = { + @CachePut(value = "topic", key = "#topic.uid") + }) + public Topic save(Topic topic) { + return topicRepository.save(topic); + } + + // TODO: 需要从原先uid的缓存列表中删除,然后添加到新的uid的换成列表中 + // @CachePut(value = "topic", key = "#uid") + public void update(String tid, String uid) { + Optional optionalTopic = findByTid(tid); + optionalTopic.ifPresent(topic -> { + topic.setUid(uid); + topicRepository.save(topic); + }); + } + + @CacheEvict(value = "topic", key = "#topic.uid") + public void delete(Topic topic) { + topicRepository.delete(topic); + } + + public TopicResponse convertToTopicResponse(Topic topic) { + return modelMapper.map(topic, TopicResponse.class); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/UidAutoConfigure.java b/modules/core/src/main/java/com/bytedesk/core/uid/UidAutoConfigure.java new file mode 100644 index 0000000000..5b34c35c82 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/UidAutoConfigure.java @@ -0,0 +1,64 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-07 15:11:16 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.uid; + +import com.bytedesk.core.uid.impl.CachedUidGenerator; +import com.bytedesk.core.uid.impl.UidProperties; +import com.bytedesk.core.uid.worker.DisposableWorkerIdAssigner; +import com.bytedesk.core.uid.worker.WorkerIdAssigner; +import com.bytedesk.core.uid.impl.DefaultUidGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * https://github.com/wujun234/uid-generator-spring-boot-starter + * UID 的自动配置 + * + * @author wujun + * @date 2019.02.20 10:57 + */ +@Configuration +@ConditionalOnClass({ DefaultUidGenerator.class, CachedUidGenerator.class }) +@EnableConfigurationProperties(UidProperties.class) +public class UidAutoConfigure { + + @Autowired + private UidProperties uidProperties; + + @Bean + @ConditionalOnMissingBean + @Lazy + DefaultUidGenerator defaultUidGenerator() { + return new DefaultUidGenerator(uidProperties); + } + + @Bean + @ConditionalOnMissingBean + @Lazy + CachedUidGenerator cachedUidGenerator() { + return new CachedUidGenerator(uidProperties); + } + + @Bean + @ConditionalOnMissingBean + WorkerIdAssigner workerIdAssigner() { + return new DisposableWorkerIdAssigner(); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/UidGenerator.java b/modules/core/src/main/java/com/bytedesk/core/uid/UidGenerator.java new file mode 100644 index 0000000000..97d24a2e0e --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/UidGenerator.java @@ -0,0 +1,78 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-13 11:37:17 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.uid; + +import java.util.Date; + +import com.bytedesk.core.uid.worker.WorkerNodeType; +import com.bytedesk.core.utils.AuditModel; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Entity for M_WORKER_NODE + * + * @author yutianbao + */ +@Entity +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "core_uid_generator") +public class UidGenerator extends AuditModel { + + /** + * Entity unique id (table unique) + */ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + /** + * Type of CONTAINER: HostName, ACTUAL : IP. + */ + private String host; + + /** + * Type of CONTAINER: Port, ACTUAL : Timestamp + Random(0-10000) + */ + private String port; + + /** + * type of {@link WorkerNodeType} + */ + private int type; + + /** + * Worker launch date, default now + */ + @Builder.Default + private Date launchDate = new Date(); + + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/UidGeneratorService.java b/modules/core/src/main/java/com/bytedesk/core/uid/UidGeneratorService.java new file mode 100644 index 0000000000..a8b245b125 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/UidGeneratorService.java @@ -0,0 +1,44 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 16:38:40 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.uid; + +import com.bytedesk.core.uid.exception.UidGenerateException; + +/** + * https://github.com/wujun234/uid-generator-spring-boot-starter + * Represents a unique id generator. + * + * @author yutianbao + */ +public interface UidGeneratorService { + + /** + * Get a unique ID + * + * @return UID + * @throws UidGenerateException + */ + long getUID() throws UidGenerateException; + + /** + * Parse the UID into elements which are used to generate the UID.
+ * Such as timestamp & workerId & sequence... + * + * @param uid + * @return Parsed info + */ + String parseUID(long uid); + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/UidGereratorRepository.java b/modules/core/src/main/java/com/bytedesk/core/uid/UidGereratorRepository.java new file mode 100644 index 0000000000..8455732dd4 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/UidGereratorRepository.java @@ -0,0 +1,54 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-07 15:48:40 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +/** + * DAO for M_WORKER_NODE + * + * @author yutianbao + * @author wujun + */ +@Repository +public interface UidGereratorRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * Get {@link UidGenerator} by node host + * + * @param host + * @param port + * @return + */ + UidGenerator findByHostAndPort(String host, String port); + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/UidUtils.java b/modules/core/src/main/java/com/bytedesk/core/uid/UidUtils.java new file mode 100644 index 0000000000..c4caf0b1d6 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/UidUtils.java @@ -0,0 +1,70 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-07 15:39:15 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 17:49:09 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.uid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.uid.impl.CachedUidGenerator; +import com.bytedesk.core.uid.impl.DefaultUidGenerator; + +// import lombok.extern.slf4j.Slf4j; + +/** + * https://github.com/wujun234/uid-generator-spring-boot-starter/blob/master/README.md + * UidGenerator接口提供了 UID 生成和解析的方法,提供了两种实现: + */ +// @Slf4j +@Component +public class UidUtils { + + @Autowired + private DefaultUidGenerator defaultUidGenerator; + + @Autowired + private CachedUidGenerator cachedUidGenerator; + + /** + * 实时生成 + * 性能有损耗 + * + * @return string + */ + public String getDefaultSerialUid() { + // Generate UID + long uid = defaultUidGenerator.getUID(); + // Parse UID into [Timestamp, WorkerId, Sequence] + // {"UID":"1165810429067392","timestamp":"2023-07-17 12:17:13","workerId":"1","sequence":"0"} + // log.info(defaultUidGenerator.parseUID(uid)); + return String.valueOf(uid); + } + + + /** + * 生成一次id之后,按序列号+1生成一批id,缓存,供之后请求 + * 如对UID生成性能有要求, 请使用CachedUidGenerator + * + * @return string + */ + public String getCacheSerialUid() { + // Generate UID + long uid = cachedUidGenerator.getUID(); + // Parse UID into [Timestamp, WorkerId, Sequence] + // {"UID":"1165809330159873","timestamp":"2023-07-17 12:15:02","workerId":"2","sequence":"1"} + // log.info(cachedUidGenerator.parseUID(uid)); + return String.valueOf(uid); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferPaddingExecutor.java b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferPaddingExecutor.java new file mode 100644 index 0000000000..8039957986 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferPaddingExecutor.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.buffer; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.bytedesk.core.uid.utils.NamingThreadFactory; +import com.bytedesk.core.uid.utils.PaddedAtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +/** + * Represents an executor for padding {@link RingBuffer}
+ * There are two kinds of executors: one for scheduled padding, the other for padding immediately. + * + * @author yutianbao + */ +public class BufferPaddingExecutor { + private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class); + + /** Constants */ + private static final String WORKER_NAME = "RingBuffer-Padding-Worker"; + private static final String SCHEDULE_NAME = "RingBuffer-Padding-Schedule"; + /** 5 minutes */ + private static final long DEFAULT_SCHEDULE_INTERVAL = 5 * 60L; + + /** Whether buffer padding is running */ + private final AtomicBoolean running; + + /** We can borrow UIDs from the future, here store the last second we have consumed */ + private final PaddedAtomicLong lastSecond; + + /** RingBuffer & BufferUidProvider */ + private final RingBuffer ringBuffer; + private final BufferedUidProvider uidProvider; + + /** Padding immediately by the thread pool */ + private final ExecutorService bufferPadExecutors; + /** Padding schedule thread */ + private final ScheduledExecutorService bufferPadSchedule; + + /** Schedule interval Unit as seconds */ + private long scheduleInterval = DEFAULT_SCHEDULE_INTERVAL; + + /** + * Constructor with {@link RingBuffer} and {@link BufferedUidProvider}, default use schedule + * + * @param ringBuffer {@link RingBuffer} + * @param uidProvider {@link BufferedUidProvider} + */ + public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider) { + this(ringBuffer, uidProvider, true); + } + + /** + * Constructor with {@link RingBuffer}, {@link BufferedUidProvider}, and whether use schedule padding + * + * @param ringBuffer {@link RingBuffer} + * @param uidProvider {@link BufferedUidProvider} + * @param usingSchedule + */ + public BufferPaddingExecutor(RingBuffer ringBuffer, BufferedUidProvider uidProvider, boolean usingSchedule) { + this.running = new AtomicBoolean(false); + this.lastSecond = new PaddedAtomicLong(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); + this.ringBuffer = ringBuffer; + this.uidProvider = uidProvider; + + // initialize thread pool + int cores = Runtime.getRuntime().availableProcessors(); + bufferPadExecutors = Executors.newFixedThreadPool(cores * 2, new NamingThreadFactory(WORKER_NAME)); + + // initialize schedule thread + if (usingSchedule) { + bufferPadSchedule = Executors.newSingleThreadScheduledExecutor(new NamingThreadFactory(SCHEDULE_NAME)); + } else { + bufferPadSchedule = null; + } + } + + /** + * Start executors such as schedule + */ + public void start() { + if (bufferPadSchedule != null) { + bufferPadSchedule.scheduleWithFixedDelay(() -> paddingBuffer(), scheduleInterval, scheduleInterval, TimeUnit.SECONDS); + } + } + + /** + * Shutdown executors + */ + public void shutdown() { + if (!bufferPadExecutors.isShutdown()) { + bufferPadExecutors.shutdownNow(); + } + + if (bufferPadSchedule != null && !bufferPadSchedule.isShutdown()) { + bufferPadSchedule.shutdownNow(); + } + } + + /** + * Whether is padding + * + * @return + */ + public boolean isRunning() { + return running.get(); + } + + /** + * Padding buffer in the thread pool + */ + public void asyncPadding() { + bufferPadExecutors.submit(this::paddingBuffer); + } + + /** + * Padding buffer fill the slots until to catch the cursor + */ + public void paddingBuffer() { + LOGGER.info("Ready to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer); + + // is still running + if (!running.compareAndSet(false, true)) { + LOGGER.info("Padding buffer is still running. {}", ringBuffer); + return; + } + + // fill the rest slots until to catch the cursor + boolean isFullRingBuffer = false; + while (!isFullRingBuffer) { + List uidList = uidProvider.provide(lastSecond.incrementAndGet()); + for (Long uid : uidList) { + isFullRingBuffer = !ringBuffer.put(uid); + if (isFullRingBuffer) { + break; + } + } + } + + // not running now + running.compareAndSet(true, false); + LOGGER.info("End to padding buffer lastSecond:{}. {}", lastSecond.get(), ringBuffer); + } + + /** + * Setters + */ + public void setScheduleInterval(long scheduleInterval) { + Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!"); + this.scheduleInterval = scheduleInterval; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferedUidProvider.java b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferedUidProvider.java new file mode 100644 index 0000000000..8e1602ad81 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/BufferedUidProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.buffer; + +import java.util.List; + +/** + * Buffered UID provider(Lambda supported), which provides UID in the same one second + * + * @author yutianbao + */ +@FunctionalInterface +public interface BufferedUidProvider { + + /** + * Provides UID in one second + * + * @param momentInSecond + * @return + */ + List provide(long momentInSecond); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedPutBufferHandler.java b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedPutBufferHandler.java new file mode 100644 index 0000000000..c6b2e4f9fd --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedPutBufferHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.buffer; + +/** + * If tail catches the cursor it means that the ring buffer is full, any more buffer put request will be rejected. + * Specify the policy to handle the reject. This is a Lambda supported interface + * + * @author yutianbao + */ +@FunctionalInterface +public interface RejectedPutBufferHandler { + + /** + * Reject put buffer request + * + * @param ringBuffer + * @param uid + */ + void rejectPutBuffer(RingBuffer ringBuffer, long uid); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedTakeBufferHandler.java b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedTakeBufferHandler.java new file mode 100644 index 0000000000..7ca9a33c1d --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RejectedTakeBufferHandler.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.buffer; + +/** + * If cursor catches the tail it means that the ring buffer is empty, any more buffer take request will be rejected. + * Specify the policy to handle the reject. This is a Lambda supported interface + * + * @author yutianbao + */ +@FunctionalInterface +public interface RejectedTakeBufferHandler { + + /** + * Reject take buffer request + * + * @param ringBuffer + */ + void rejectTakeBuffer(RingBuffer ringBuffer); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RingBuffer.java b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RingBuffer.java new file mode 100644 index 0000000000..3fc4342aa8 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/buffer/RingBuffer.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.buffer; + +import java.util.concurrent.atomic.AtomicLong; + +import com.bytedesk.core.uid.utils.PaddedAtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +/** + * Represents a ring buffer based on array.
+ * Using array could improve read element performance due to the CUP cache line. To prevent + * the side effect of False Sharing, {@link PaddedAtomicLong} is using on 'tail' and 'cursor'

+ * + * A ring buffer is consisted of: + *

  • slots: each element of the array is a slot, which is be set with a UID + *
  • flags: flag array corresponding the same index with the slots, indicates whether can take or put slot + *
  • tail: a sequence of the max slot position to produce + *
  • cursor: a sequence of the min slot position to consume + * + * @author yutianbao + */ +public class RingBuffer { + private static final Logger LOGGER = LoggerFactory.getLogger(RingBuffer.class); + + /** Constants */ + private static final int START_POINT = -1; + private static final long CAN_PUT_FLAG = 0L; + private static final long CAN_TAKE_FLAG = 1L; + public static final int DEFAULT_PADDING_PERCENT = 50; + + /** The size of RingBuffer's slots, each slot hold a UID */ + private final int bufferSize; + private final long indexMask; + private final long[] slots; + private final PaddedAtomicLong[] flags; + + /** Tail: last position sequence to produce */ + private final AtomicLong tail = new PaddedAtomicLong(START_POINT); + + /** Cursor: current position sequence to consume */ + private final AtomicLong cursor = new PaddedAtomicLong(START_POINT); + + /** Threshold for trigger padding buffer*/ + private final int paddingThreshold; + + /** Reject put/take buffer handle policy */ + private RejectedPutBufferHandler rejectedPutHandler = this::discardPutBuffer; + private RejectedTakeBufferHandler rejectedTakeHandler = this::exceptionRejectedTakeBuffer; + + /** Executor of padding buffer */ + private BufferPaddingExecutor bufferPaddingExecutor; + + /** + * Constructor with buffer size, paddingFactor default as {@value #DEFAULT_PADDING_PERCENT} + * + * @param bufferSize must be positive & a power of 2 + */ + public RingBuffer(int bufferSize) { + this(bufferSize, DEFAULT_PADDING_PERCENT); + } + + /** + * Constructor with buffer size & padding factor + * + * @param bufferSize must be positive & a power of 2 + * @param paddingFactor percent in (0 - 100). When the count of rest available UIDs reach the threshold, it will trigger padding buffer
    + * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, + * padding buffer will be triggered when tail-cursor 0L, "RingBuffer size must be positive"); + Assert.isTrue(Integer.bitCount(bufferSize) == 1, "RingBuffer size must be a power of 2"); + Assert.isTrue(paddingFactor > 0 && paddingFactor < 100, "RingBuffer size must be positive"); + + this.bufferSize = bufferSize; + this.indexMask = bufferSize - 1; + this.slots = new long[bufferSize]; + this.flags = initFlags(bufferSize); + + this.paddingThreshold = bufferSize * paddingFactor / 100; + } + + /** + * Put an UID in the ring & tail moved
    + * We use 'synchronized' to guarantee the UID fill in slot & publish new tail sequence as atomic operations
    + * + * Note that: It is recommended to put UID in a serialize way, cause we once batch generate a series UIDs and put + * the one by one into the buffer, so it is unnecessary put in multi-threads + * + * @param uid + * @return false means that the buffer is full, apply {@link RejectedPutBufferHandler} + */ + public synchronized boolean put(long uid) { + long currentTail = tail.get(); + long currentCursor = cursor.get(); + + // tail catches the cursor, means that you can't put any cause of RingBuffer is full + long distance = currentTail - (currentCursor == START_POINT ? 0 : currentCursor); + if (distance == bufferSize - 1) { + rejectedPutHandler.rejectPutBuffer(this, uid); + return false; + } + + // 1. pre-check whether the flag is CAN_PUT_FLAG + int nextTailIndex = calSlotIndex(currentTail + 1); + if (flags[nextTailIndex].get() != CAN_PUT_FLAG) { + rejectedPutHandler.rejectPutBuffer(this, uid); + return false; + } + + // 2. put UID in the next slot + // 3. update next slot' flag to CAN_TAKE_FLAG + // 4. publish tail with sequence increase by one + slots[nextTailIndex] = uid; + flags[nextTailIndex].set(CAN_TAKE_FLAG); + tail.incrementAndGet(); + + // The atomicity of operations above, guarantees by 'synchronized'. In another word, + // the take operation can't consume the UID we just put, until the tail is published(tail.incrementAndGet()) + return true; + } + + /** + * Take an UID of the ring at the next cursor, this is a lock free operation by using atomic cursor

    + * + * Before getting the UID, we also check whether reach the padding threshold, + * the padding buffer operation will be triggered in another thread
    + * If there is no more available UID to be taken, the specified {@link RejectedTakeBufferHandler} will be applied
    + * + * @return UID + * @throws IllegalStateException if the cursor moved back + */ + public long take() { + // spin get next available cursor + long currentCursor = cursor.get(); + long nextCursor = cursor.updateAndGet(old -> old == tail.get() ? old : old + 1); + + // check for safety consideration, it never occurs + Assert.isTrue(nextCursor >= currentCursor, "Curosr can't move back"); + + // trigger padding in an async-mode if reach the threshold + long currentTail = tail.get(); + if (currentTail - nextCursor < paddingThreshold) { + LOGGER.info("Reach the padding threshold:{}. tail:{}, cursor:{}, rest:{}", paddingThreshold, currentTail, + nextCursor, currentTail - nextCursor); + bufferPaddingExecutor.asyncPadding(); + } + + // cursor catch the tail, means that there is no more available UID to take + if (nextCursor == currentCursor) { + rejectedTakeHandler.rejectTakeBuffer(this); + } + + // 1. check next slot flag is CAN_TAKE_FLAG + int nextCursorIndex = calSlotIndex(nextCursor); + Assert.isTrue(flags[nextCursorIndex].get() == CAN_TAKE_FLAG, "Curosr not in can take status"); + + // 2. get UID from next slot + // 3. set next slot flag as CAN_PUT_FLAG. + long uid = slots[nextCursorIndex]; + flags[nextCursorIndex].set(CAN_PUT_FLAG); + + // Note that: Step 2,3 can not swap. If we set flag before get value of slot, the producer may overwrite the + // slot with a new UID, and this may cause the consumer take the UID twice after walk a round the ring + return uid; + } + + /** + * Calculate slot index with the slot sequence (sequence % bufferSize) + */ + protected int calSlotIndex(long sequence) { + return (int) (sequence & indexMask); + } + + /** + * Discard policy for {@link RejectedPutBufferHandler}, we just do logging + */ + protected void discardPutBuffer(RingBuffer ringBuffer, long uid) { + LOGGER.warn("Rejected putting buffer for uid:{}. {}", uid, ringBuffer); + } + + /** + * Policy for {@link RejectedTakeBufferHandler}, throws {@link RuntimeException} after logging + */ + protected void exceptionRejectedTakeBuffer(RingBuffer ringBuffer) { + LOGGER.warn("Rejected take buffer. {}", ringBuffer); + throw new RuntimeException("Rejected take buffer. " + ringBuffer); + } + + /** + * Initialize flags as CAN_PUT_FLAG + */ + private PaddedAtomicLong[] initFlags(int bufferSize) { + PaddedAtomicLong[] flags = new PaddedAtomicLong[bufferSize]; + for (int i = 0; i < bufferSize; i++) { + flags[i] = new PaddedAtomicLong(CAN_PUT_FLAG); + } + + return flags; + } + + /** + * Getters + */ + public long getTail() { + return tail.get(); + } + + public long getCursor() { + return cursor.get(); + } + + public int getBufferSize() { + return bufferSize; + } + + /** + * Setters + */ + public void setBufferPaddingExecutor(BufferPaddingExecutor bufferPaddingExecutor) { + this.bufferPaddingExecutor = bufferPaddingExecutor; + } + + public void setRejectedPutHandler(RejectedPutBufferHandler rejectedPutHandler) { + this.rejectedPutHandler = rejectedPutHandler; + } + + public void setRejectedTakeHandler(RejectedTakeBufferHandler rejectedTakeHandler) { + this.rejectedTakeHandler = rejectedTakeHandler; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RingBuffer [bufferSize=").append(bufferSize) + .append(", tail=").append(tail) + .append(", cursor=").append(cursor) + .append(", paddingThreshold=").append(paddingThreshold).append("]"); + + return builder.toString(); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/exception/UidGenerateException.java b/modules/core/src/main/java/com/bytedesk/core/uid/exception/UidGenerateException.java new file mode 100644 index 0000000000..1878b608c5 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/exception/UidGenerateException.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.exception; + +/** + * UidGenerateException + * + * @author yutianbao + */ +public class UidGenerateException extends RuntimeException { + + /** + * Serial Version UID + */ + private static final long serialVersionUID = -27048199131316992L; + + /** + * Default constructor + */ + public UidGenerateException() { + super(); + } + + /** + * Constructor with message & cause + * + * @param message + * @param cause + */ + public UidGenerateException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor with message + * + * @param message + */ + public UidGenerateException(String message) { + super(message); + } + + /** + * Constructor with message format + * + * @param msgFormat + * @param args + */ + public UidGenerateException(String msgFormat, Object... args) { + super(String.format(msgFormat, args)); + } + + /** + * Constructor with cause + * + * @param cause + */ + public UidGenerateException(Throwable cause) { + super(cause); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/impl/BitsAllocator.java b/modules/core/src/main/java/com/bytedesk/core/uid/impl/BitsAllocator.java new file mode 100644 index 0000000000..a1c964a0f2 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/impl/BitsAllocator.java @@ -0,0 +1,110 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-07 15:37:54 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.impl; + +import org.springframework.util.Assert; + +import lombok.Data; +import lombok.ToString; + +/** + * Allocate 64 bits for the UID(long)
    + * sign (fixed 1bit) -> deltaSecond -> workerId -> sequence(within the same second) + * + * @author yutianbao + */ +@ToString +@Data +public class BitsAllocator { + /** + * Total 64 bits + */ + public static final int TOTAL_BITS = 1 << 6; + + /** + * Bits for [sign-> second-> workId-> sequence] + */ + private int signBits = 1; + private final int timestampBits; + private final int workerIdBits; + private final int sequenceBits; + + /** + * Max value for workId & sequence + */ + private final long maxDeltaSeconds; + private final long maxWorkerId; + private final long maxSequence; + + /** + * Shift for timestamp & workerId + */ + private final int timestampShift; + private final int workerIdShift; + + /** + * Constructor with timestampBits, workerIdBits, sequenceBits
    + * The highest bit used for sign, so 63 bits for timestampBits, workerIdBits, sequenceBits + */ + public BitsAllocator(int timestampBits, int workerIdBits, int sequenceBits) { + // make sure allocated 64 bits + int allocateTotalBits = signBits + timestampBits + workerIdBits + sequenceBits; + Assert.isTrue(allocateTotalBits <= TOTAL_BITS, "allocate larger than 64 bits"); + + // initialize bits + this.timestampBits = timestampBits; + this.workerIdBits = workerIdBits; + this.sequenceBits = sequenceBits; + + // initialize max value + this.maxDeltaSeconds = ~(-1L << timestampBits); + this.maxWorkerId = ~(-1L << workerIdBits); + this.maxSequence = ~(-1L << sequenceBits); + + // initialize shift + this.timestampShift = workerIdBits + sequenceBits; + this.workerIdShift = sequenceBits; + } + + /** + * Allocate bits for UID according to delta seconds & workerId & sequence
    + * Note that: The highest bit will always be 0 for sign + * + * @param deltaSeconds + * @param workerId + * @param sequence + * @return + */ + public long allocate(long deltaSeconds, long workerId, long sequence) { + return (deltaSeconds << timestampShift) | (workerId << workerIdShift) | sequence; + } + + +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/impl/CachedUidGenerator.java b/modules/core/src/main/java/com/bytedesk/core/uid/impl/CachedUidGenerator.java new file mode 100644 index 0000000000..eb194ec317 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/impl/CachedUidGenerator.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.impl; + +import java.util.ArrayList; +import java.util.List; + +import com.bytedesk.core.uid.UidGeneratorService; +import com.bytedesk.core.uid.exception.UidGenerateException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.Assert; + +import com.bytedesk.core.uid.buffer.BufferPaddingExecutor; +import com.bytedesk.core.uid.buffer.RejectedPutBufferHandler; +import com.bytedesk.core.uid.buffer.RejectedTakeBufferHandler; +import com.bytedesk.core.uid.buffer.RingBuffer; + +/** + * Represents a cached implementation of {@link UidGeneratorService} extends + * from {@link DefaultUidGenerator}, based on a lock free {@link RingBuffer}

    + *

    + * The spring properties you can specified as below:
    + *

  • boostPower: RingBuffer size boost for a power of 2, Sample: boostPower is 3, it means the buffer size + * will be ({@link BitsAllocator#getMaxSequence()} + 1) << + * {@link #boostPower}, Default as {@value #DEFAULT_BOOST_POWER} + *
  • paddingFactor: Represents a percent value of (0 - 100). When the count of rest available UIDs reach the + * threshold, it will trigger padding buffer. Default as{@link RingBuffer#DEFAULT_PADDING_PERCENT} + * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, padding buffer will be triggered when tail-cursorscheduleInterval: Padding buffer in a schedule, specify padding buffer interval, Unit as second + *
  • rejectedPutBufferHandler: Policy for rejected put buffer. Default as discard put request, just do logging + *
  • rejectedTakeBufferHandler: Policy for rejected take buffer. Default as throwing up an exception + * + * @author yutianbao + * @author wujun + */ +@ConfigurationProperties(prefix = "bytedesk.uid.cached-uid-generator") +public class CachedUidGenerator extends DefaultUidGenerator implements DisposableBean { + private static final Logger LOGGER = LoggerFactory.getLogger(CachedUidGenerator.class); + private static final int DEFAULT_BOOST_POWER = 3; + + // --------------------- 配置属性 begin --------------------- + /** + * RingBuffer size扩容参数, 可提高UID生成的吞吐量. + * 默认:3, 原bufferSize=8192, 扩容后bufferSize= 8192 << 3 = 65536 + */ + private int boostPower = DEFAULT_BOOST_POWER; + /** + * 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50 + * 举例: bufferSize=1024, paddingFactor=50 -> threshold=1024 * 50 / 100 = 512. + * 当环上可用UID数量 < 512时, 将自动对RingBuffer进行填充补全 + */ + private int paddingFactor = RingBuffer.DEFAULT_PADDING_PERCENT; + /** + * 另外一种RingBuffer填充时机, 在Schedule线程中, 周期性检查填充 + * 默认:不配置此项, 即不使用Schedule线程. 如需使用, 请指定Schedule线程时间间隔, 单位:秒 + */ + private Long scheduleInterval; + // --------------------- 配置属性 end ----------------------- + + /** 拒绝策略: 当环已满, 无法继续填充时 + 默认无需指定, 将丢弃Put操作, 仅日志记录. 如有特殊需求, 请实现RejectedPutBufferHandler接口(支持Lambda表达式)并以@Autowired方式注入 */ + @Autowired(required = false) + private RejectedPutBufferHandler rejectedPutBufferHandler; + + /** 拒绝策略: 当环已空, 无法继续获取时 + 默认无需指定, 将记录日志, 并抛出UidGenerateException异常. 如有特殊需求, 请实现RejectedTakeBufferHandler接口(支持Lambda表达式)并以@Autowired方式注入 */ + @Autowired(required = false) + private RejectedTakeBufferHandler rejectedTakeBufferHandler; + + /** + * RingBuffer + */ + private RingBuffer ringBuffer; + private BufferPaddingExecutor bufferPaddingExecutor; + + public CachedUidGenerator(UidProperties uidProperties) { + super(uidProperties); + } + + @Override + public void afterPropertiesSet() throws Exception { + // initialize workerId & bitsAllocator + super.afterPropertiesSet(); + + // initialize RingBuffer & RingBufferPaddingExecutor + this.initRingBuffer(); + LOGGER.info("Initialized RingBuffer successfully."); + } + + @Override + public long getUID() { + try { + return ringBuffer.take(); + } catch (Exception e) { + LOGGER.error("Generate unique id exception. ", e); + throw new UidGenerateException(e); + } + } + + @Override + public String parseUID(long uid) { + return super.parseUID(uid); + } + + @Override + public void destroy() throws Exception { + bufferPaddingExecutor.shutdown(); + } + + /** + * Get the UIDs in the same specified second under the max sequence + * + * @param currentSecond + * @return UID list, size of {@link BitsAllocator#getMaxSequence()} + 1 + */ + protected List nextIdsForOneSecond(long currentSecond) { + // Initialize result list size of (max sequence + 1) + int listSize = (int) bitsAllocator.getMaxSequence() + 1; + List uidList = new ArrayList<>(listSize); + + // Allocate the first sequence of the second, the others can be calculated with the offset + long firstSeqUid = bitsAllocator.allocate(currentSecond - uidProperties.getEpochSeconds(), workerId, 0L); + for (int offset = 0; offset < listSize; offset++) { + uidList.add(firstSeqUid + offset); + } + + return uidList; + } + + /** + * Initialize RingBuffer & RingBufferPaddingExecutor + */ + private void initRingBuffer() { + // initialize RingBuffer + int bufferSize = ((int) bitsAllocator.getMaxSequence() + 1) << boostPower; + this.ringBuffer = new RingBuffer(bufferSize, paddingFactor); + LOGGER.info("Initialized ring buffer size:{}, paddingFactor:{}", bufferSize, paddingFactor); + + // initialize RingBufferPaddingExecutor + boolean usingSchedule = (scheduleInterval != null); + this.bufferPaddingExecutor = new BufferPaddingExecutor(ringBuffer, this::nextIdsForOneSecond, usingSchedule); + if (usingSchedule) { + bufferPaddingExecutor.setScheduleInterval(scheduleInterval); + } + + LOGGER.info("Initialized BufferPaddingExecutor. Using schdule:{}, interval:{}", usingSchedule, scheduleInterval); + + // set rejected put/take handle policy + this.ringBuffer.setBufferPaddingExecutor(bufferPaddingExecutor); + if (rejectedPutBufferHandler != null) { + this.ringBuffer.setRejectedPutHandler(rejectedPutBufferHandler); + } + if (rejectedTakeBufferHandler != null) { + this.ringBuffer.setRejectedTakeHandler(rejectedTakeBufferHandler); + } + + // fill in all slots of the RingBuffer + bufferPaddingExecutor.paddingBuffer(); + + // start buffer padding threads + bufferPaddingExecutor.start(); + } + + /** + * Setters for spring property + */ + public void setBoostPower(int boostPower) { + Assert.isTrue(boostPower > 0, "Boost power must be positive!"); + this.boostPower = boostPower; + } + + public void setPaddingFactor(int paddingFactor) { + Assert.isTrue(paddingFactor > 0 && paddingFactor < 100, "padding factor must be in (0, 100)!"); + this.paddingFactor = paddingFactor; + } + + public void setRejectedPutBufferHandler(RejectedPutBufferHandler rejectedPutBufferHandler) { + Assert.notNull(rejectedPutBufferHandler, "RejectedPutBufferHandler can't be null!"); + this.rejectedPutBufferHandler = rejectedPutBufferHandler; + } + + public void setRejectedTakeBufferHandler(RejectedTakeBufferHandler rejectedTakeBufferHandler) { + Assert.notNull(rejectedTakeBufferHandler, "RejectedTakeBufferHandler can't be null!"); + this.rejectedTakeBufferHandler = rejectedTakeBufferHandler; + } + + public void setScheduleInterval(long scheduleInterval) { + Assert.isTrue(scheduleInterval > 0, "Schedule interval must positive!"); + this.scheduleInterval = scheduleInterval; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/impl/DefaultUidGenerator.java b/modules/core/src/main/java/com/bytedesk/core/uid/impl/DefaultUidGenerator.java new file mode 100644 index 0000000000..d8ae2a040d --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/impl/DefaultUidGenerator.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.impl; + +import java.util.concurrent.TimeUnit; + +import com.bytedesk.core.uid.UidGeneratorService; +import com.bytedesk.core.uid.exception.UidGenerateException; +import com.bytedesk.core.uid.worker.WorkerIdAssigner; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Represents an implementation of {@link UidGeneratorService} + *

    + * The unique id has 64bits (long), default allocated as blow:
    + *

  • sign: The highest bit is 0 + *
  • delta seconds: The next 28 bits, represents delta seconds since a customer epoch(2016-05-20 00:00:00.000). + * Supports about 8.7 years until to 2024-11-20 21:24:16 + *
  • worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W + *
  • sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s

    + *

    + * The {@link DefaultUidGenerator#parseUID(long)} is a tool method to parse the bits + *

    + *

    {@code
    + * +------+----------------------+----------------+-----------+
    + * | sign |     delta seconds    | worker node id | sequence  |
    + * +------+----------------------+----------------+-----------+
    + *   1bit          28bits              22bits         13bits
    + * }
    + *

    + * You can also specified the bits by Spring property setting. + *

  • timeBits: default as 28 + *
  • workerBits: default as 22 + *
  • seqBits: default as 13 + *
  • epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'

    + *

    + * Note that: The total bits must be 64 -1 + * + * @author yutianbao + * @author wujun + */ +@Slf4j +public class DefaultUidGenerator implements UidGeneratorService, InitializingBean { + // private static final Logger log = LoggerFactory.getLogger(DefaultUidGenerator.class); + + protected UidProperties uidProperties; + + /** + * Bit分配器,Stable fields after spring bean initializing + */ + protected BitsAllocator bitsAllocator; + protected long workerId; + + /** + * Volatile fields caused by nextId() + */ + protected long sequence = 0L; + protected long lastSecond = -1L; + + /** + * Spring property + */ + @Autowired + protected WorkerIdAssigner workerIdAssigner; + + public DefaultUidGenerator(UidProperties uidProperties) { + this.uidProperties = uidProperties; + } + + @Override + public void afterPropertiesSet() throws Exception { + // initialize bits allocator + bitsAllocator = new BitsAllocator(uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits()); + + // initialize worker id + workerId = workerIdAssigner.assignWorkerId(); + if (workerId > bitsAllocator.getMaxWorkerId()) { + log.error("Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId()); + workerId = workerId % bitsAllocator.getMaxWorkerId(); + log.info("new Worker id = " + workerId); + } + + log.info("Initialized bits(1, {}, {}, {}) for workerID:{}", uidProperties.getTimeBits(), uidProperties.getWorkerBits(), uidProperties.getSeqBits(), workerId); + } + + @Override + public long getUID() throws UidGenerateException { + try { + return nextId(); + } catch (Exception e) { + log.error("Generate unique id exception. ", e); + throw new UidGenerateException(e); + } + } + + @Override + public String parseUID(long uid) { + long totalBits = BitsAllocator.TOTAL_BITS; + long workerIdBits = bitsAllocator.getWorkerIdBits(); + long sequenceBits = bitsAllocator.getSequenceBits(); + + // parse UID + long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits); + long workerId = (uid << (totalBits - workerIdBits - sequenceBits)) >>> (totalBits - workerIdBits); + // long deltaSeconds = uid >>> (workerIdBits + sequenceBits); + + // Date thatTime = new Date(TimeUnit.SECONDS.toMillis(uidProperties.getEpochSeconds() + deltaSeconds)); + // String thatTimeStr = DateUtils.formatByDateTimePattern(thatTime); + + // format as string + return String.format("{\"UID\":\"%d\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"sequence\":\"%d\"}", + uid, + "", // thatTimeStr, + workerId, + sequence); + } + + /** + * Get UID + * + * @return UID + * @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp + */ + protected synchronized long nextId() { + long currentSecond = getCurrentSecond(); + + // Clock moved backwards, refuse to generate uid + if (currentSecond < lastSecond) { + long refusedSeconds = lastSecond - currentSecond; + if (uidProperties.isEnableBackward()) { + if (refusedSeconds <= uidProperties.getMaxBackwardSeconds()) { + log.error("Clock moved backwards. wait for %d seconds", refusedSeconds); + while (currentSecond < lastSecond) { + currentSecond = getCurrentSecond(); + } + } else { + workerId = workerIdAssigner.assignFakeWorkerId(); + log.error("Clock moved backwards. Assigned New WorkerId %d", workerId); + if (workerId > bitsAllocator.getMaxWorkerId()) { + log.error("Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId()); + workerId = workerId % bitsAllocator.getMaxWorkerId(); + log.info("new Worker id = " + workerId); + } + } + } else { + throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds); + } + } + + // At the same second, increase sequence + if (currentSecond == lastSecond) { + sequence = (sequence + 1) & bitsAllocator.getMaxSequence(); + // Exceed the max sequence, we wait the next second to generate uid + if (sequence == 0) { + currentSecond = getNextSecond(lastSecond); + } + + // At the different second, sequence restart from zero + } else { + sequence = 0L; + } + + lastSecond = currentSecond; + + // Allocate bits for UID + return bitsAllocator.allocate(currentSecond - uidProperties.getEpochSeconds(), workerId, sequence); + } + + /** + * Get next second + */ + private long getNextSecond(long lastTimestamp) { + long timestamp = getCurrentSecond(); + while (timestamp <= lastTimestamp) { + timestamp = getCurrentSecond(); + } + + return timestamp; + } + + /** + * Get current second + */ + private long getCurrentSecond() { + long currentSecond = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); + if (currentSecond - uidProperties.getEpochSeconds() > bitsAllocator.getMaxDeltaSeconds()) { + throw new UidGenerateException("Timestamp bits is exhausted. Refusing UID generate. Now: " + currentSecond); + } + + return currentSecond; + } + + /** + * Setters for spring property + */ + public void setWorkerIdAssigner(WorkerIdAssigner workerIdAssigner) { + this.workerIdAssigner = workerIdAssigner; + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/impl/UidProperties.java b/modules/core/src/main/java/com/bytedesk/core/uid/impl/UidProperties.java new file mode 100644 index 0000000000..eca659652b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/impl/UidProperties.java @@ -0,0 +1,75 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-07 15:40:50 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.uid.impl; + +import lombok.Data; + +import java.util.concurrent.TimeUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * UID 的配置 + * + * @author wujun + * @date 2019.02.20 10:31 + */ +@Data +@ConfigurationProperties(prefix = "bytedesk.uid") +public class UidProperties { + + /** + * 时间增量值占用位数。当前时间相对于时间基点的增量值,单位为秒 + */ + private int timeBits = 30; + + /** + * 工作机器ID占用的位数 + */ + private int workerBits = 16; + + /** + * 序列号占用的位数 + */ + private int seqBits = 7; + + /** + * 时间基点. 例如 2019-02-20 (毫秒: 1550592000000) + */ + private String epochStr = "2019-02-20"; + + /** + * 时间基点对应的毫秒数 + */ + private long epochSeconds = TimeUnit.MILLISECONDS.toSeconds(1550592000000L); + + /** + * 是否容忍时钟回拨, 默认:true + */ + private boolean enableBackward = true; + + /** + * 时钟回拨最长容忍时间(秒) + */ + private long maxBackwardSeconds = 1L; + + // public void setEpochStr(String epochStr) { + // if (StringUtils.hasText(epochStr)) { + // this.epochStr = epochStr; + // this.epochSeconds = TimeUnit.MILLISECONDS.toSeconds(DateUtils.parseByDayPattern(epochStr).getTime()); + // } + // } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/readme.md b/modules/core/src/main/java/com/bytedesk/core/uid/readme.md new file mode 100644 index 0000000000..55daa45f10 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/readme.md @@ -0,0 +1,17 @@ + +# uidgenerator-spring-boot-starter + +- adapted to spring boot3 [uid-generator-spring-boot-starter](https://github.com/wujun234/uid-generator-spring-boot-starter) diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/DateUtils.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/DateUtils.java new file mode 100644 index 0000000000..58d645af38 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/DateUtils.java @@ -0,0 +1,122 @@ +// /* +// * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// package com.bytedesk.core.uid.utils; + +// import java.text.ParseException; +// import java.util.Calendar; +// import java.util.Date; + +// import org.apache.commons.lang3.time.DateFormatUtils; + + +// /** +// * DateUtils provides date formatting, parsing +// * +// * @author yutianbao +// */ +// public abstract class DateUtils { +// /** +// * Patterns +// */ +// public static final String DAY_PATTERN = "yyyy-MM-dd"; +// public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; +// public static final String DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; + +// public static final Date DEFAULT_DATE = DateUtils.parseByDayPattern("1970-01-01"); + +// /** +// * Parse date by 'yyyy-MM-dd' pattern +// * +// * @param str +// * @return +// */ +// public static Date parseByDayPattern(String str) { +// return parseDate(str, DAY_PATTERN); +// } + +// /** +// * Parse date by 'yyyy-MM-dd HH:mm:ss' pattern +// * +// * @param str +// * @return +// */ +// public static Date parseByDateTimePattern(String str) { +// return parseDate(str, DATETIME_PATTERN); +// } + +// /** +// * Parse date without Checked exception +// * +// * @param str +// * @param pattern +// * @return +// * @throws RuntimeException when ParseException occurred +// */ +// public static Date parseDate(String str, String pattern) { +// try { +// return parseDate(str, new String[]{pattern}); +// } catch (ParseException e) { +// throw new RuntimeException(e); +// } +// } + +// /** +// * Format date into string +// * +// * @param date +// * @param pattern +// * @return +// */ +// public static String formatDate(Date date, String pattern) { +// return DateFormatUtils.format(date, pattern); +// } + +// /** +// * Format date by 'yyyy-MM-dd' pattern +// * +// * @param date +// * @return +// */ +// public static String formatByDayPattern(Date date) { +// if (date != null) { +// return DateFormatUtils.format(date, DAY_PATTERN); +// } else { +// return null; +// } +// } + +// /** +// * Format date by 'yyyy-MM-dd HH:mm:ss' pattern +// * +// * @param date +// * @return +// */ +// public static String formatByDateTimePattern(Date date) { +// return DateFormatUtils.format(date, DATETIME_PATTERN); +// } + +// /** +// * Get current day using format date by 'yyyy-MM-dd HH:mm:ss' pattern +// * +// * @return +// * @author yebo +// */ +// public static String getCurrentDayByDayPattern() { +// Calendar cal = Calendar.getInstance(); +// return formatByDayPattern(cal.getTime()); +// } + +// } diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/DockerUtils.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/DockerUtils.java new file mode 100644 index 0000000000..560a97bdcb --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/DockerUtils.java @@ -0,0 +1,104 @@ +// /* +// * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// package com.bytedesk.core.uid.utils; + +// import org.apache.commons.lang.StringUtils; +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; + +// /** +// * DockerUtils +// * +// * @author yutianbao +// */ +// public abstract class DockerUtils { +// private static final Logger LOGGER = LoggerFactory.getLogger(DockerUtils.class); + +// /** Environment param keys */ +// private static final String ENV_KEY_HOST = "JPAAS_HOST"; +// private static final String ENV_KEY_PORT = "JPAAS_HTTP_PORT"; +// private static final String ENV_KEY_PORT_ORIGINAL = "JPAAS_HOST_PORT_8080"; + +// /** Docker host & port */ +// private static String DOCKER_HOST = ""; +// private static String DOCKER_PORT = ""; + +// /** Whether is docker */ +// private static boolean IS_DOCKER; + +// static { +// retrieveFromEnv(); +// } + +// /** +// * Retrieve docker host +// * +// * @return empty string if not a docker +// */ +// public static String getDockerHost() { +// return DOCKER_HOST; +// } + +// /** +// * Retrieve docker port +// * +// * @return empty string if not a docker +// */ +// public static String getDockerPort() { +// return DOCKER_PORT; +// } + +// /** +// * Whether a docker +// * +// * @return +// */ +// public static boolean isDocker() { +// return IS_DOCKER; +// } + +// /** +// * Retrieve host & port from environment +// */ +// private static void retrieveFromEnv() { +// // retrieve host & port from environment +// DOCKER_HOST = System.getenv(ENV_KEY_HOST); +// DOCKER_PORT = System.getenv(ENV_KEY_PORT); + +// // not found from 'JPAAS_HTTP_PORT', then try to find from 'JPAAS_HOST_PORT_8080' +// if (StringUtils.isBlank(DOCKER_PORT)) { +// DOCKER_PORT = System.getenv(ENV_KEY_PORT_ORIGINAL); +// } + +// boolean hasEnvHost = StringUtils.isNotBlank(DOCKER_HOST); +// boolean hasEnvPort = StringUtils.isNotBlank(DOCKER_PORT); + +// // docker can find both host & port from environment +// if (hasEnvHost && hasEnvPort) { +// IS_DOCKER = true; + +// // found nothing means not a docker, maybe an actual machine +// } else if (!hasEnvHost && !hasEnvPort) { +// IS_DOCKER = false; + +// } else { +// LOGGER.error("Missing host or port from env for Docker. host:{}, port:{}", DOCKER_HOST, DOCKER_PORT); +// throw new RuntimeException( +// "Missing host or port from env for Docker. host:" + DOCKER_HOST + ", port:" + DOCKER_PORT); +// } +// } + +// } diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/EnumUtils.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/EnumUtils.java new file mode 100644 index 0000000000..4174c062f3 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/EnumUtils.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.utils; + +import org.springframework.util.Assert; + +/** + * EnumUtils provides the operations for {@link ValuedEnum} such as Parse, value of... + * + * @author yutianbao + */ +public abstract class EnumUtils { + + /** + * Parse the bounded value into ValuedEnum + * + * @param clz + * @param value + * @return + */ + public static , V> T parse(Class clz, V value) { + Assert.notNull(clz, "clz can not be null"); + if (value == null) { + return null; + } + + for (T t : clz.getEnumConstants()) { + if (value.equals(t.value())) { + return t; + } + } + return null; + } + + /** + * Null-safe valueOf function + * + * @param + * @param enumType + * @param name + * @return + */ + public static > T valueOf(Class enumType, String name) { + if (name == null) { + return null; + } + + return Enum.valueOf(enumType, name); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/NamingThreadFactory.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/NamingThreadFactory.java new file mode 100644 index 0000000000..b6833019be --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/NamingThreadFactory.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.utils; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +// import org.apache.commons.lang.ClassUtils; +import org.springframework.util.StringUtils; + +import lombok.extern.slf4j.Slf4j; + +/** + * Named thread in ThreadFactory. If there is no specified name for thread, it + * will auto detect using the invoker classname instead. + * + * @author yutianbao + */ +@Slf4j +public class NamingThreadFactory implements ThreadFactory { + + /** + * Thread name pre + */ + private String name; + /** + * Is daemon thread + */ + private boolean daemon; + /** + * UncaughtExceptionHandler + */ + private UncaughtExceptionHandler uncaughtExceptionHandler; + /** + * Sequences for multi thread name prefix + */ + private final ConcurrentHashMap sequences; + + /** + * Constructors + */ + public NamingThreadFactory() { + this(null, false, null); + } + + public NamingThreadFactory(String name) { + this(name, false, null); + } + + public NamingThreadFactory(String name, boolean daemon) { + this(name, daemon, null); + } + + public NamingThreadFactory(String name, boolean daemon, UncaughtExceptionHandler handler) { + this.name = name; + this.daemon = daemon; + this.uncaughtExceptionHandler = handler; + this.sequences = new ConcurrentHashMap(); + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(this.daemon); + + // If there is no specified name for thread, it will auto detect using the invoker classname instead. + // Notice that auto detect may cause some performance overhead + String prefix = this.name; + if (!StringUtils.hasText(prefix)) { + prefix = getInvoker(2); + } + thread.setName(prefix + "-" + getSequence(prefix)); + + // no specified uncaughtExceptionHandler, just do logging. + if (this.uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(this.uncaughtExceptionHandler); + } else { + thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + log.error("unhandled exception in thread: " + t.getId() + ":" + t.getName(), e); + } + }); + } + + return thread; + } + + /** + * Get the method invoker's class name + * + * @param depth + * @return + */ + private String getInvoker(int depth) { + // Exception e = new Exception(); + // StackTraceElement[] stes = e.getStackTrace(); + // if (stes.length > depth) { + // return ClassUtils.getShortClassName(stes[depth].getClassName()); + // } + return getClass().getSimpleName(); + } + + /** + * Get sequence for different naming prefix + * + * @param invoker + * @return + */ + private long getSequence(String invoker) { + AtomicLong r = this.sequences.get(invoker); + if (r == null) { + r = new AtomicLong(0); + AtomicLong previous = this.sequences.putIfAbsent(invoker, r); + if (previous != null) { + r = previous; + } + } + + return r.incrementAndGet(); + } + + /** + * Getters & Setters + */ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean daemon) { + this.daemon = daemon; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { + this.uncaughtExceptionHandler = handler; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/NetUtils.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/NetUtils.java new file mode 100644 index 0000000000..bf00e4e251 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/NetUtils.java @@ -0,0 +1,115 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 16:48:18 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.utils; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; + +/** + * NetUtils + * + * @author yutianbao + */ +public abstract class NetUtils { + + /** + * Pre-loaded local address + */ + public static InetAddress localAddress; + + static { + try { + localAddress = getLocalInetAddress(); + } catch (SocketException e) { + // throw new RuntimeException("fail to get local ip."); + } + } + + /** + * Retrieve the first validated local ip address(the Public and LAN ip addresses are validated). + * + * @return the local address + * @throws SocketException the socket exception + */ + public static InetAddress getLocalInetAddress() throws SocketException { + // enumerates all network interfaces + Enumeration enu = NetworkInterface.getNetworkInterfaces(); + + while (enu.hasMoreElements()) { + NetworkInterface ni = enu.nextElement(); + if (ni.isLoopback()) { + continue; + } + + Enumeration addressEnumeration = ni.getInetAddresses(); + while (addressEnumeration.hasMoreElements()) { + InetAddress address = addressEnumeration.nextElement(); + + // ignores all invalidated addresses + if (address.isLinkLocalAddress() || address.isLoopbackAddress() || address.isAnyLocalAddress()) { + continue; + } + + return address; + } + } + + // throw new RuntimeException("No validated local address!"); + try { + return InetAddress.getByName("127.0.0.1"); + } catch (UnknownHostException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return null; + } + + + public static InetAddress getLocalAddressInstance() { + return localAddress; + } + /** + * Retrieve local address + * + * @return the string local address + */ + public static String getLocalAddress() { + return localAddress.getHostAddress(); + } + + public static String getHostname() { + return localAddress.getHostAddress(); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/PaddedAtomicLong.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/PaddedAtomicLong.java new file mode 100644 index 0000000000..7ef63aea2c --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/PaddedAtomicLong.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.utils; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Represents a padded {@link AtomicLong} to prevent the FalseSharing problem

    + * + * The CPU cache line commonly be 64 bytes, here is a sample of cache line after padding:
    + * 64 bytes = 8 bytes (object reference) + 6 * 8 bytes (padded long) + 8 bytes (a long value) + * + * @author yutianbao + */ +public class PaddedAtomicLong extends AtomicLong { + private static final long serialVersionUID = -3415778863941386253L; + + /** Padded 6 long (48 bytes) */ + public volatile long p1, p2, p3, p4, p5, p6 = 7L; + + /** + * Constructors from {@link AtomicLong} + */ + public PaddedAtomicLong() { + super(); + } + + public PaddedAtomicLong(long initialValue) { + super(initialValue); + } + + /** + * To prevent GC optimizations for cleaning unused padded references + */ + public long sumPaddingToPreventOptimization() { + return p1 + p2 + p3 + p4 + p5 + p6; + } + +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/utils/ValuedEnum.java b/modules/core/src/main/java/com/bytedesk/core/uid/utils/ValuedEnum.java new file mode 100644 index 0000000000..c7f160442b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/utils/ValuedEnum.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.utils; + +/** + * {@code ValuedEnum} defines an enumeration which is bounded to a value, you + * may implements this interface when you defines such kind of enumeration, that + * you can use {@link EnumUtils} to simplify parse and valueOf operation. + * + * @author yutianbao + */ +public interface ValuedEnum { + /** + * get value + * + * @return T + */ + T value(); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/worker/DisposableWorkerIdAssigner.java b/modules/core/src/main/java/com/bytedesk/core/uid/worker/DisposableWorkerIdAssigner.java new file mode 100644 index 0000000000..d295c64253 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/worker/DisposableWorkerIdAssigner.java @@ -0,0 +1,120 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2021-02-24 15:52:39 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 16:59:02 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bytedesk.core.uid.worker; + +import com.bytedesk.core.uid.UidGenerator; +import com.bytedesk.core.uid.UidGereratorRepository; +// import com.bytedesk.core.uid.utils.DockerUtils; +import com.bytedesk.core.uid.utils.NetUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +// import org.apache.commons.lang.math.RandomUtils; +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +/** + * Represents an implementation of {@link WorkerIdAssigner}, + * the worker id will be discarded after assigned to the UidGenerator + * + * @author yutianbao + */ +public class DisposableWorkerIdAssigner implements WorkerIdAssigner { + + // @Value("${server.host}") + // private String host; + + @Value("${server.port}") + private String port; + + // @Resource + @Autowired + private UidGereratorRepository workerNodeDAO; + + /** + * Assign worker id base on database.

    + * If there is host name & port in the environment, we considered that the node runs in Docker container
    + * Otherwise, the node runs on an actual machine. + * + * @return assigned worker id + */ + @Transactional(rollbackFor = Exception.class) + @Override + public long assignWorkerId() { + // build worker node entity + UidGenerator workerNodeEntity = buildWorkerNode(); + + UidGenerator oldWorkerNode = workerNodeDAO + .findByHostAndPort(workerNodeEntity.getHost(), workerNodeEntity.getPort()); + if (null != oldWorkerNode) { + return oldWorkerNode.getId(); + } + + // add worker node for new (ignore the same IP + PORT) + workerNodeDAO.save(workerNodeEntity); + // LOGGER.info("Add worker node:" + workerNodeEntity); + + return workerNodeEntity.getId(); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public long assignFakeWorkerId() { + return buildFakeWorkerNode().getId(); + } + + /** + * Build worker node entity by IP and PORT + */ + private UidGenerator buildWorkerNode() { + UidGenerator workerNodeEntity = new UidGenerator(); + + workerNodeEntity.setType(WorkerNodeType.ACTUAL.value()); + workerNodeEntity.setHost(NetUtils.getLocalAddress()); + // workerNodeEntity.setPort(System.currentTimeMillis() + "-" + new Random().nextInt(100000)); + // workerNodeEntity.setHost(host); + workerNodeEntity.setPort(port); + + return workerNodeEntity; + } + + private UidGenerator buildFakeWorkerNode() { + UidGenerator workerNodeEntity = new UidGenerator(); + workerNodeEntity.setType(WorkerNodeType.FAKE.value()); + workerNodeEntity.setHost(NetUtils.getLocalAddress()); + // workerNodeEntity.setPort(System.currentTimeMillis() + "-" + new Random().nextInt(100000)); + // workerNodeEntity.setHost(host); + workerNodeEntity.setPort(port); + + return workerNodeEntity; + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerIdAssigner.java b/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerIdAssigner.java new file mode 100644 index 0000000000..1af46c991b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerIdAssigner.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.worker; + +import com.bytedesk.core.uid.impl.DefaultUidGenerator; + +/** + * Represents a worker id assigner for {@link DefaultUidGenerator} + * + * @author yutianbao + */ +public interface WorkerIdAssigner { + + /** + * Assign worker id for {@link DefaultUidGenerator} + * + * @return assigned worker id + */ + long assignWorkerId(); + + /** + * Assign fake worker id + * + * @return assigned fake worker id + */ + long assignFakeWorkerId(); + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerNodeType.java b/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerNodeType.java new file mode 100644 index 0000000000..84eba87b22 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/uid/worker/WorkerNodeType.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Baidu, Inc. All Rights Reserve. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bytedesk.core.uid.worker; + +import com.bytedesk.core.uid.utils.ValuedEnum; + +/** + * WorkerNodeType + *

  • CONTAINER: Such as Docker + *
  • ACTUAL: Actual machine + * + * @author yutianbao + */ +public enum WorkerNodeType implements ValuedEnum { + + /** + * 容器 + */ + CONTAINER(1), + /** + * 物理机 + */ + ACTUAL(2), + /** + * 虚拟 + */ + FAKE(3); + + /** + * Lock type + */ + private final Integer type; + + /** + * Constructor with field of type + */ + private WorkerNodeType(Integer type) { + this.type = type; + } + + @Override + public Integer value() { + return type; + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/upload/UploadService.java b/modules/core/src/main/java/com/bytedesk/core/upload/UploadService.java index 8457309c8b..a381f6a9b5 100755 --- a/modules/core/src/main/java/com/bytedesk/core/upload/UploadService.java +++ b/modules/core/src/main/java/com/bytedesk/core/upload/UploadService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-15 11:35:53 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-23 13:39:01 + * @LastEditTime: 2024-04-17 11:33:34 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -94,7 +94,7 @@ public class UploadService { return rootLocation.resolve(filename); } - @SuppressWarnings("null") + // @SuppressWarnings("null") public Resource loadAsResource(String filename) { try { Path file = load(filename); diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/ApplicationContextHolder.java b/modules/core/src/main/java/com/bytedesk/core/utils/ApplicationContextHolder.java new file mode 100644 index 0000000000..654aad1793 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/utils/ApplicationContextHolder.java @@ -0,0 +1,32 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-22 23:32:19 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 23:32:22 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class ApplicationContextHolder implements ApplicationContextAware { + private static ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + public static T getBean(Class beanClass) { + return context.getBean(beanClass); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/AuditModel.java b/modules/core/src/main/java/com/bytedesk/core/utils/AuditModel.java index 5b756d8f7d..61a811a287 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/AuditModel.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/AuditModel.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 12:46:27 + * @LastEditTime: 2024-04-18 12:35:49 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesa * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,29 +14,31 @@ */ package com.bytedesk.core.utils; -import com.fasterxml.jackson.annotation.JsonFormat; - import jakarta.persistence.Column; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; -import org.springframework.beans.factory.annotation.Value; +// import org.springframework.beans.factory.annotation.Value; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +// import com.fasterxml.jackson.annotation.JsonFormat; + import lombok.Data; -// import lombok.Builder; -// import lombok.Getter; -// import lombok.Setter; import java.io.Serializable; +// import java.time.ZonedDateTime; import java.util.Date; /** + * Date -> ZonedDateTime ? + * spring.jackson.date-format=yyyy-MM-dd HH:mm:ss + * spring.jackson.time-zone=GMT+8 * + * @see https://docs.spring.io/spring-data/jpa/reference/auditing.html * @author im.bytedesk.com */ @Data @@ -44,22 +46,22 @@ import java.util.Date; @EntityListeners(AuditingEntityListener.class) public class AuditModel implements Serializable { - private static final long serialVersionUID = 6798032420558915191L; - - @Value("${bytedesk.timezone}") - private static final String timezone = "GMT+8"; + // @Value("${bytedesk.timezone}") + // private static final String timezone = "GMT+8"; @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_at", updatable = false) @CreatedDate - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = timezone) + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = timezone) // config in properties private Date createdAt; + // private ZonedDateTime createdAt; @Temporal(TemporalType.TIMESTAMP) @Column(name = "updated_at") @LastModifiedDate - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = timezone) + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = timezone) // config in properties private Date updatedAt; + // private ZonedDateTime updatedAt; /** * soft delete diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/BaseRequest.java b/modules/core/src/main/java/com/bytedesk/core/utils/BaseRequest.java index ba3b5ad0e2..5dad808b6f 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/BaseRequest.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/BaseRequest.java @@ -23,16 +23,16 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = false) public class BaseRequest implements Serializable { - Long id; + public Long id; - int pageNumber; + public int pageNumber; - int pageSize; + public int pageSize; - String type; + public String type; - String content; + public String content; - String client; + public String client; } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/BaseResponse.java b/modules/core/src/main/java/com/bytedesk/core/utils/BaseResponse.java index c1ad04b001..676875ec1e 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/BaseResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/BaseResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 14:45:48 + * @LastEditTime: 2024-04-11 16:32:08 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,7 @@ package com.bytedesk.core.utils; import java.io.Serializable; + public class BaseResponse implements Serializable { } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/BdConvertUtils.java b/modules/core/src/main/java/com/bytedesk/core/utils/BdConvertUtils.java index a3cb6e32dd..7d07dc9e25 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/BdConvertUtils.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/BdConvertUtils.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-01 17:20:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 11:06:19 + * @LastEditTime: 2024-04-11 11:53:16 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,7 @@ package com.bytedesk.core.utils; import org.modelmapper.ModelMapper; +import com.alibaba.fastjson2.JSON; import com.bytedesk.core.message.Message; import com.bytedesk.core.message.MessageResponse; import com.bytedesk.core.rbac.role.Role; @@ -39,7 +40,9 @@ public class BdConvertUtils { MessageResponse messageResponse = new ModelMapper().map(message, MessageResponse.class); - messageResponse.setUser(convertTUserResponseSimple(message.getUser())); + UserResponseSimple user = JSON.parseObject(message.getUser(), UserResponseSimple.class); + messageResponse.setUser(user); + // messageResponse.setUser(convertTUserResponseSimple(message.getUser())); return messageResponse; } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/BdDateUtils.java b/modules/core/src/main/java/com/bytedesk/core/utils/BdDateUtils.java index 29060cc4aa..b937247902 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/BdDateUtils.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/BdDateUtils.java @@ -15,7 +15,6 @@ import org.springframework.util.StringUtils; public class BdDateUtils { private BdDateUtils() { - } private static final String datetimeFormat = "yyyy-MM-dd HH:mm:ss"; diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/JsonResult.java b/modules/core/src/main/java/com/bytedesk/core/utils/JsonResult.java index 5a809003cc..fbb4e05a9c 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/JsonResult.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/JsonResult.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 09:45:50 + * @LastEditTime: 2024-04-22 21:08:37 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -52,8 +52,12 @@ public class JsonResult implements Serializable { return new JsonResult().setCode(200).setMessage("success").setData(data); } - public static JsonResult success(String message, int statusCode, T data) { - return new JsonResult().setCode(statusCode).setMessage(message).setData(data); + public static JsonResult success(String message, T data) { + return new JsonResult().setCode(200).setMessage(message).setData(data); + } + + public static JsonResult success(String message, int code, T data) { + return new JsonResult().setCode(code).setMessage(message).setData(data); } public static JsonResult error() { @@ -64,7 +68,11 @@ public class JsonResult implements Serializable { return new JsonResult().setCode(500).setMessage(message).setData(false); } - public static JsonResult error(String message, int statusCode, T data) { - return new JsonResult().setCode(statusCode).setMessage(message).setData(data); + public static JsonResult error(String message, int code) { + return new JsonResult().setCode(code).setMessage(message).setData(false); + } + + public static JsonResult error(String message, int code, T data) { + return new JsonResult().setCode(code).setMessage(message).setData(data); } } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/JwtUtils.java b/modules/core/src/main/java/com/bytedesk/core/utils/JwtUtils.java index f5d42bd809..48c30cd59a 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/JwtUtils.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/JwtUtils.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-07 22:42:26 + * @LastEditTime: 2024-04-27 11:55:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,10 +14,10 @@ */ package com.bytedesk.core.utils; -// import javax.crypto.SecretKey; -import java.security.Key; import java.util.Date; +import javax.crypto.SecretKey; + import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -26,6 +26,9 @@ import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; +/** + * https://github.com/jwtk/jjwt#jws-create-key + */ @Slf4j @Component // @AllArgsConstructor @@ -37,56 +40,30 @@ public class JwtUtils { @Value("${bytedesk.jwt-expiration}") private long jwtExpirationMs; - // @Autowired - // private BytedeskProperties bytedeskProperties; - - @SuppressWarnings("deprecation") + // @SuppressWarnings("deprecation") + /** + * https://github.com/jwtk/jjwt?tab=readme-ov-file#creating-a-jwt + * @param username + * @return + */ public String generateJwtToken(String username) { - // log.info("generateJwtToken: {}, key {}, expiration {}", - // username, - // bytedeskProperties.getJwtSecretKey(), - // bytedeskProperties.getJwtExpiration() - // ); - return Jwts.builder() .subject((username)) .issuedAt(new Date()) .expiration(new Date((new Date()).getTime() + jwtExpirationMs)) - .signWith(key(), - SignatureAlgorithm.HS256) + // .signWith(key(), SignatureAlgorithm.HS256) + .signWith(secretKey()) .compact(); } - // public String generateJwtToken(Authentication authentication) { - // UserDetailsImpl userPrincipal = (UserDetailsImpl) - // authentication.getPrincipal(); - // return Jwts.builder() - // .subject((userPrincipal.getUsername())) - // .issuedAt(new Date()) - // .expiration(new Date((new Date()).getTime() + jwtExpirationMs)) - // .signWith(key(), - // SignatureAlgorithm.HS256) // SignatureAlgorithm.HS256 - // .compact(); - // } - - // private SecretKey key() { - // return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); - // } - - private Key key() { - return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); - } - - @SuppressWarnings("deprecation") - public String getUserNameFromJwtToken(String token) { - return Jwts.parser().setSigningKey(key()).build() - .parseSignedClaims(token).getPayload().getSubject(); - } - - @SuppressWarnings("deprecation") + // @SuppressWarnings("deprecation") public boolean validateJwtToken(String authToken) { try { - Jwts.parser().setSigningKey(key()).build().parse(authToken); + Jwts.parser() + // .setSigningKey(key()) + .verifyWith(secretKey()) + .build() + .parse(authToken); return true; } catch (MalformedJwtException e) { log.error("Invalid JWT token: {}", e.getMessage()); @@ -100,4 +77,23 @@ public class JwtUtils { return false; } + + // @SuppressWarnings("deprecation") + public String getUserNameFromJwtToken(String token) { + return Jwts.parser() + // .setSigningKey(key()) + .verifyWith(secretKey()) + .build() + .parseSignedClaims(token) + .getPayload() + .getSubject(); + } + + // private Key key() { + // return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); + // } + + private SecretKey secretKey() { + return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/StringSetConverter.java b/modules/core/src/main/java/com/bytedesk/core/utils/StringSetConverter.java new file mode 100644 index 0000000000..db5c2d1d22 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/utils/StringSetConverter.java @@ -0,0 +1,47 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 16:14:33 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 16:14:51 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.utils; + +import com.google.common.base.Strings; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +@Converter +public class StringSetConverter implements AttributeConverter, String> { + + @Override + public String convertToDatabaseColumn(Set set) { + Iterator iterator = set.iterator(); + while(iterator.hasNext()){ + String str = iterator.next(); + if(Strings.isNullOrEmpty(str)){ + iterator.remove(); // 正确 + } + } + return String.join(",", set); + } + + @Override + public Set convertToEntityAttribute(String joined) { + return joined == null ? new HashSet<>() : new HashSet<>(Arrays.asList(joined.split(","))); + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/Utils.java b/modules/core/src/main/java/com/bytedesk/core/utils/Utils.java index a0b29d345f..5b55af2037 100644 --- a/modules/core/src/main/java/com/bytedesk/core/utils/Utils.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/Utils.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-01 10:22:19 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-01 10:22:30 + * @LastEditTime: 2024-04-25 15:58:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,10 @@ */ package com.bytedesk.core.utils; +import com.bytedesk.core.utils.id.DefaultIdGenerator; +import com.bytedesk.core.utils.id.IdGenerator; + +import java.util.Random; import java.util.UUID; public class Utils { @@ -21,7 +25,69 @@ public class Utils { private Utils() { } + /** + * ID 生成器 + */ + private static final IdGenerator ID_GENERATOR = new DefaultIdGenerator(); + + /** + * 根据时间戳,生成随机唯一id 如:202009151044291 + * 必须只能返回数字串,不能包含字母标点符号等 + * + * @return 随机 + */ + public static String timeSerialId() { + return ID_GENERATOR.next(); + } + + /** + * UUID + * + * @return UUID + */ + public static String uuid() { + return UUID.randomUUID().toString(); + } + + /** + * 带有时间戳的uuid + * UUID.randomUUID().toString()长度为36,我们去掉‘-’之后,截取前半段 + * + * @return uuid + */ + public static String timeUuid() { + return ID_GENERATOR.next() + UUID.randomUUID().toString().replace("-", "");// .substring(0, 18); + } + public static String getUid() { return UUID.randomUUID().toString().replaceAll("-", ""); } + + + /** + * for test + * + * @param mobile + * @return + */ + public static boolean isTestMobile(String mobile) { + return mobile.startsWith("1888888"); + } + + /** + * + * @return + */ + public static String getRandomCode(String key) { + int code = 123456; + if (isTestMobile(key)) { + code = 123456; + } else { + int min = 100001; + int max = 999998; + code = new Random().nextInt(max) % (max - min + 1) + min; + } + return String.valueOf(code); + } + } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AlreadyExistsException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/AlreadyExistsException.java deleted file mode 100755 index 283ab9134f..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AlreadyExistsException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -/** - * Exception caused by entity existence already. - * - * @author johnniang - */ -public class AlreadyExistsException extends BadRequestException { - - /** - * - */ - private static final long serialVersionUID = 185895443794683426L; - - public AlreadyExistsException(String message) { - super(message); - } - - public AlreadyExistsException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ApiErrorResponse.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/ApiErrorResponse.java deleted file mode 100644 index 63040fafa3..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ApiErrorResponse.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.http.HttpStatus; - -@Getter -@Setter -public class ApiErrorResponse { - - private HttpStatus status; - private String error_code; - private String message; - private String detail; - - // getter and setters - // Builder - public static final class ApiErrorResponseBuilder { - private HttpStatus status; - private String error_code; - private String message; - private String detail; - - public ApiErrorResponseBuilder() { - } - - public static ApiErrorResponseBuilder anApiErrorResponse() { - return new ApiErrorResponseBuilder(); - } - - public ApiErrorResponseBuilder withStatus(HttpStatus status) { - this.status = status; - return this; - } - - public ApiErrorResponseBuilder withError_code(String error_code) { - this.error_code = error_code; - return this; - } - - public ApiErrorResponseBuilder withMessage(String message) { - this.message = message; - return this; - } - - public ApiErrorResponseBuilder withDetail(String detail) { - this.detail = detail; - return this; - } - - public ApiErrorResponse build() { - ApiErrorResponse apiErrorResponse = new ApiErrorResponse(); - apiErrorResponse.status = this.status; - apiErrorResponse.error_code = this.error_code; - apiErrorResponse.detail = this.detail; - apiErrorResponse.message = this.message; - return apiErrorResponse; - } - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AppNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/AppNotFoundException.java deleted file mode 100644 index 24baaed7f8..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AppNotFoundException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class AppNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -3987082031613316940L; - - public AppNotFoundException(String aid) { - super("app not found"); - log.error("app {} not fount", aid); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AuthorityNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/AuthorityNotFoundException.java deleted file mode 100644 index afaa771d4f..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/AuthorityNotFoundException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class AuthorityNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -5856696470241765976L; - - public AuthorityNotFoundException(Long id) { - super("role not found"); - log.error("role {} not fount", id); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BadRequestException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/BadRequestException.java deleted file mode 100755 index dc14da6724..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BadRequestException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; - -/** - * Exception caused by bad request. - * - * @author johnniang - */ -public class BadRequestException extends BytedeskException { - - /** - * - */ - private static final long serialVersionUID = 8146006124058491585L; - - public BadRequestException(String message) { - super(message); - } - - public BadRequestException(String message, Throwable cause) { - super(message, cause); - } - - @Override - public @NonNull HttpStatus getStatus() { - return HttpStatus.BAD_REQUEST; - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BeanUtilsException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/BeanUtilsException.java deleted file mode 100755 index ed830c6289..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BeanUtilsException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; - -/** - * BeanUtils exception. - * - * @author johnniang - */ -public class BeanUtilsException extends BytedeskException { - - private static final long serialVersionUID = -2693716689479955009L; - - public BeanUtilsException(String message) { - super(message); - } - - public BeanUtilsException(String message, Throwable cause) { - super(message, cause); - } - - @Override - public @NonNull HttpStatus getStatus() { - return HttpStatus.INTERNAL_SERVER_ERROR; - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BytedeskException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/BytedeskException.java deleted file mode 100755 index a5c4b7aa8b..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/BytedeskException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; - -/** - * Base exception of the project. - * - * @author johnniang - */ -public abstract class BytedeskException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -3730280146845163419L; - /** - * Error errorData. - */ - private Object errorData; - - public BytedeskException(String message) { - super(message); - } - - public BytedeskException(String message, Throwable cause) { - super(message, cause); - } - - @NonNull - public abstract HttpStatus getStatus(); - - @Nullable - public Object getErrorData() { - return errorData; - } - - /** - * Sets error errorData. - * - * @param errorData error data - * @return current exception. - */ - @NonNull - public BytedeskException setErrorData(@Nullable Object errorData) { - this.errorData = errorData; - return this; - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/DepartmentNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/DepartmentNotFoundException.java deleted file mode 100644 index 1512d5d0cf..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/DepartmentNotFoundException.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class DepartmentNotFoundException extends RuntimeException { - - private static final long serialVersionUID = -8502840684293721269L; - - public DepartmentNotFoundException(String did) { - super("department not found"); - log.error("department {} not fount", did); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ForbiddenException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/ForbiddenException.java deleted file mode 100755 index 8f28c148b6..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ForbiddenException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.lang.NonNull; - -/** - * Exception caused by accessing forbidden resources. - * - * @author johnniang - */ -public class ForbiddenException extends BytedeskException { - - /** - * - */ - private static final long serialVersionUID = -6029126336570526306L; - - public ForbiddenException(String message) { - super(message); - } - - public ForbiddenException(String message, Throwable cause) { - super(message, cause); - } - - @Override - public @NonNull HttpStatus getStatus() { - return HttpStatus.FORBIDDEN; - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ImageFormatException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/ImageFormatException.java deleted file mode 100644 index d9c69b800f..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/ImageFormatException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -/** - * Image format exception. - * - * @author ZhiXiang Yuan - * @date 2020/08/10 02:11 - */ -public class ImageFormatException extends BadRequestException { - - public ImageFormatException(String message) { - super(message); - } - - public ImageFormatException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/MenuNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/MenuNotFoundException.java deleted file mode 100644 index c242190288..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/MenuNotFoundException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class MenuNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 2945447432978388479L; - - public MenuNotFoundException(String mid) { - super("menu not found"); - log.error("menu {} not fount", mid); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/PrincipalNullException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/PrincipalNullException.java deleted file mode 100644 index bde6416e30..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/PrincipalNullException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class PrincipalNullException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -6426450230129602873L; - - public PrincipalNullException() { - log.error("principal is null"); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/RoleNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/RoleNotFoundException.java deleted file mode 100644 index e1956a76ae..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/RoleNotFoundException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RoleNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 5646024396772307621L; - - public RoleNotFoundException(Long id) { - super("role not found"); - log.error("role {} not fount", id); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/exception/UserNotFoundException.java b/modules/core/src/main/java/com/bytedesk/core/utils/exception/UserNotFoundException.java deleted file mode 100644 index f3eb31a89b..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/utils/exception/UserNotFoundException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ -package com.bytedesk.core.utils.exception; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class UserNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -6690095132444054097L; - - public UserNotFoundException(String username) { - super("username not found"); - log.error("username {} not fount", username); - } -} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGenerator.java b/modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGenerator.java new file mode 100644 index 0000000000..7e00ac982b --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGenerator.java @@ -0,0 +1,78 @@ +package com.bytedesk.core.utils.id; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 默认的ID生成器, 采用前缀+时间+原子数的形式实现 + * 建议相同的配置采用同一个实例 + * @see IdGeneratorConfig + * @author Ivan.Ma + */ +public class DefaultIdGenerator implements IdGenerator, Runnable{ + + private String time; + + private AtomicInteger value; + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + + private IdGeneratorConfig config; + + private Thread thread; + + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public DefaultIdGenerator(){ + config = new DefaultIdGeneratorConfig(); + time = LocalDateTime.now().format(FORMATTER); + value = new AtomicInteger(config.getInitial()); + + thread = new Thread(this); + thread.setDaemon(true); + thread.start(); + } + + public DefaultIdGenerator(IdGeneratorConfig config){ + this.config = config; + time = LocalDateTime.now().format(FORMATTER); + value = new AtomicInteger(config.getInitial()); + + thread = new Thread(this); + thread.setDaemon(true); + thread.start(); + } + + @Override + public String next() { + lock.readLock().lock(); + StringBuffer sb = new StringBuffer(config.getPrefix()) + .append(config.getSplitString()) + .append(time) + .append(config.getSplitString()) + .append(value.getAndIncrement()); + lock.readLock().unlock(); + return sb.toString(); + } + + @Override + public void run() { + while (true){ + try { + Thread.sleep(1000 * config.getRollingInterval()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + String now = LocalDateTime.now().format(FORMATTER); + if (!now.equals(time)){ + lock.writeLock().lock(); + time = now; + value.set(config.getInitial()); + lock.writeLock().unlock(); + } + } + } + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/redis/RedisThreadService.java b/modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGeneratorConfig.java similarity index 61% rename from modules/core/src/main/java/com/bytedesk/core/redis/RedisThreadService.java rename to modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGeneratorConfig.java index 0f7cb77b84..ced307b8e3 100644 --- a/modules/core/src/main/java/com/bytedesk/core/redis/RedisThreadService.java +++ b/modules/core/src/main/java/com/bytedesk/core/utils/id/DefaultIdGeneratorConfig.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-02-26 13:01:41 + * @Date: 2024-04-08 12:13:12 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 13:12:23 + * @LastEditTime: 2024-04-08 12:13:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,25 +12,32 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.redis; - -import org.springframework.stereotype.Service; -import lombok.AllArgsConstructor; - -// import io.xiaper.core.publisher.EventPublisher; +package com.bytedesk.core.utils.id; /** - * 会话 redis 操作 * * @author xiaper.io */ -// @Slf4j -@Service -@AllArgsConstructor -public class RedisThreadService { +public class DefaultIdGeneratorConfig implements IdGeneratorConfig { - // private final StringRedisTemplate stringRedisTemplate; + @Override + public String getSplitString() { + return ""; + } - // private final EventPublisher eventPublisher; + @Override + public int getInitial() { + return 1; + } + + @Override + public String getPrefix() { + return ""; + } + + @Override + public int getRollingInterval() { + return 1; + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGenerator.java b/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGenerator.java new file mode 100644 index 0000000000..073364fc1a --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGenerator.java @@ -0,0 +1,17 @@ +package com.bytedesk.core.utils.id; + +/** + * + * ID生成器接口, 用于生成全局唯一的ID流水号 + * + * @author Ivan.Ma + */ +public interface IdGenerator { + + /** + * 生成下一个不重复的流水号 + * @return string + */ + String next(); + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGeneratorConfig.java b/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGeneratorConfig.java new file mode 100644 index 0000000000..2ab98e879a --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/utils/id/IdGeneratorConfig.java @@ -0,0 +1,47 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-08 12:13:12 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-08 12:13:31 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.utils.id; + +/** + * ID生成器的配置接口 + * @author Ivan.Ma + */ +public interface IdGeneratorConfig { + + /** + * 获取分隔符 + * @return string + */ + String getSplitString(); + + /** + * 获取初始值 + * @return int + */ + int getInitial(); + + /** + * 获取ID前缀 + * @return string + */ + String getPrefix(); + + /** + * 获取滚动间隔, 单位: 秒 + * @return int + */ + int getRollingInterval(); + +} diff --git a/modules/local/.DS_Store b/modules/local/.DS_Store index ebe6301f9b..257957d8f2 100644 Binary files a/modules/local/.DS_Store and b/modules/local/.DS_Store differ diff --git a/modules/local/pom.xml b/modules/local/pom.xml index debd81bab0..0bf1566f18 100644 --- a/modules/local/pom.xml +++ b/modules/local/pom.xml @@ -11,7 +11,7 @@ 0.0.1-SNAPSHOT - weiyu-local + im-local local Demo project for Spring Boot @@ -20,14 +20,21 @@ com.bytedesk - weiyu-core + im-core ${im.version} provided com.bytedesk - weiyu-socket + im-service + ${im.version} + provided + + + + com.bytedesk + im-socket ${im.version} provided @@ -51,6 +58,7 @@ + 0.0.1-SNAPSHOT @@ -18,10 +17,12 @@ ai + blog core local - oa + service social + socket team @@ -57,18 +58,13 @@ provided + org.springframework.boot spring-boot-starter-data-jpa provided - - com.mysql - mysql-connector-j - provided - - org.springframework.boot spring-boot-starter-validation @@ -87,18 +83,7 @@ provided - - org.springframework.boot - spring-boot-starter-data-redis - provided - - - - org.springframework.session - spring-session-data-redis - provided - - + org.springframework.boot spring-boot-starter-actuator @@ -126,22 +111,39 @@ true + + + + + - + + - + + + com.alibaba.fastjson2 fastjson2 - 2.0.45 + 2.0.48 + provided @@ -175,6 +177,32 @@ provided + + + + + com.google.guava + guava + 33.1.0-jre + provided + + + + + + + + + com.github.javafaker + javafaker + 1.0.2 + + org.springframework.boot @@ -202,12 +230,12 @@ - + + com.querydsl querydsl-apt @@ -224,11 +252,10 @@ target/generated-sources/queries com.querydsl.apt.jpa.JPAAnnotationProcessor - - + --> diff --git a/modules/readme.md b/modules/readme.md new file mode 100644 index 0000000000..d588a21b74 --- /dev/null +++ b/modules/readme.md @@ -0,0 +1,15 @@ + +# open source modules - 开源模块 diff --git a/modules/service/.DS_Store b/modules/service/.DS_Store index d52c55efb6..85b88949c7 100644 Binary files a/modules/service/.DS_Store and b/modules/service/.DS_Store differ diff --git a/modules/service/pom.xml b/modules/service/pom.xml index 7761f3d179..49699c5ee4 100644 --- a/modules/service/pom.xml +++ b/modules/service/pom.xml @@ -11,25 +11,30 @@ 0.0.1-SNAPSHOT - weiyu-service + im-service service Demo project for Spring Boot + + 7.4.2 + 2.6.1 + + com.bytedesk - weiyu-core + im-core ${im.version} provided com.bytedesk - weiyu-team + im-team ${im.version} provided @@ -38,6 +43,28 @@ + +-keepnames interface ** { *; } +# 此选项将保存所有软件包中的所有原始接口文件(不进行混淆) +-keep interface * extends * { *; } +# 保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数 +-keepparameternames +# 保留枚举成员及方法 +-keepclassmembers enum * { *; } +# 不混淆所有类,保存原始定义的注释- +-keepclassmembers class * { + @org.springframework.context.annotation.Bean *; + @org.springframework.beans.factory.annotation.Autowired *; + @org.springframework.beans.factory.annotation.Value *; + @org.springframework.stereotype.Service *; + @org.springframework.stereotype.Component *; + @org.springframework.web.bind.annotation.GetMapping *; + @org.springframework.web.bind.annotation.PostMapping *; + @org.springframework.web.bind.annotation.RequestBody *; + @org.springframework.web.bind.annotation.RequestMapping *; + @org.springframework.web.bind.annotation.RestController *; +} +# 忽略warn消息 +-ignorewarnings +# 打印配置信息 +-printconfiguration \ No newline at end of file diff --git a/modules/service/src/.DS_Store b/modules/service/src/.DS_Store index d9bdf88b7a..8e720825f7 100644 Binary files a/modules/service/src/.DS_Store and b/modules/service/src/.DS_Store differ diff --git a/modules/service/src/main/.DS_Store b/modules/service/src/main/.DS_Store index 8aebf79e62..229838d1f6 100644 Binary files a/modules/service/src/main/.DS_Store and b/modules/service/src/main/.DS_Store differ diff --git a/modules/local/src/.DS_Store b/modules/service/src/main/java/.DS_Store similarity index 91% rename from modules/local/src/.DS_Store rename to modules/service/src/main/java/.DS_Store index 510c9df810..c144cf568c 100644 Binary files a/modules/local/src/.DS_Store and b/modules/service/src/main/java/.DS_Store differ diff --git a/modules/ai/src/.DS_Store b/modules/service/src/main/java/com/.DS_Store similarity index 94% rename from modules/ai/src/.DS_Store rename to modules/service/src/main/java/com/.DS_Store index a477d20112..5c3e827b80 100644 Binary files a/modules/ai/src/.DS_Store and b/modules/service/src/main/java/com/.DS_Store differ diff --git a/modules/service/src/main/java/com/bytedesk/.DS_Store b/modules/service/src/main/java/com/bytedesk/.DS_Store index c2feb04e7c..9b8b361021 100644 Binary files a/modules/service/src/main/java/com/bytedesk/.DS_Store and b/modules/service/src/main/java/com/bytedesk/.DS_Store differ diff --git a/modules/service/src/main/java/com/bytedesk/service/.DS_Store b/modules/service/src/main/java/com/bytedesk/service/.DS_Store index f5ae764c23..98fa724ea3 100644 Binary files a/modules/service/src/main/java/com/bytedesk/service/.DS_Store and b/modules/service/src/main/java/com/bytedesk/service/.DS_Store differ diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/Agent.java b/modules/service/src/main/java/com/bytedesk/service/agent/Agent.java index 50a0736f39..47c85e8d7d 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/Agent.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/Agent.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:35:50 + * @LastEditTime: 2024-04-24 15:14:35 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,10 @@ */ package com.bytedesk.service.agent; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.AuditModel; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -25,10 +29,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +// import lombok.extern.slf4j.Slf4j; /** * 客服账号-关联信息 */ +// @Slf4j @Entity @Data @Builder @@ -36,6 +42,7 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor +@EntityListeners({ AgentListener.class }) @Table(name = "service_agent") public class Agent extends AuditModel { @@ -43,12 +50,12 @@ public class Agent extends AuditModel { @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - /** - * - */ - @Column(unique = true, nullable = false) - private String aid; + @Column(name = "uuid", unique = true, nullable = false) + private String uid; + /** + * visible to visitors + */ private String nickname; private String avatar; @@ -57,20 +64,71 @@ public class Agent extends AuditModel { private String email; + /** agent description */ private String description; /** - * + * @{AgentConsts} */ - // private String acceptStatus; + @Builder.Default + private String acceptStatus = AgentConsts.ACCEPT_STATUS_ACCEPTING; - // private String connectStatus; + @Builder.Default + @Column(name = "is_connected") + private boolean connected = false; + + // max concurrent chatting thread count + @Builder.Default + private Integer maxThreadCount = 10; /** - * + * tips + * TODO: set different tips for different lang + */ + @Builder.Default + private String welcomeTip = BdConstants.DEFAULT_WORK_GROUP_ACCEPT_TIP; + + /** auto close time in min - 默认自动关闭时间,单位分钟 */ + @Builder.Default + private Double autoCloseMin = Double.valueOf(25); + + /** 存储当前接待数量等 */ + @Builder.Default + @Column(columnDefinition = "json") + // 用于兼容postgreSQL,否则会报错,[ERROR: column "extra" is of type json but expression is of type character varying + @JdbcTypeCode(SqlTypes.JSON) + private String extra = BdConstants.EMPTY_JSON_STRING; + + /** + * login user info */ @JsonIgnore - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @ManyToOne(fetch = FetchType.LAZY) private User user; + /** belong to org */ + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // private Organization organization; + private String orgOid; + + /** + * belongs to user + */ + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // private User owner; + + + // @PostPersist + // public void onPostPersist() { + // // log.debug("onPostPersist: {}", this); + // // 这里可以记录日志、发送通知等 + // // create agent topic + // TopicService topicService = ApplicationContextHolder.getBean(TopicService.class); + // // + // topicService.create(this.getUid(), this.getUser().getUid()); + // } + } + diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentConsts.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentConsts.java new file mode 100644 index 0000000000..897ec6f158 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentConsts.java @@ -0,0 +1,30 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-10 10:45:35 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 14:26:41 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.agent; + +public class AgentConsts { + + private AgentConsts() { + } + + public static final String ACCEPT_STATUS_ACCEPTING = "accepting"; + public static final String ACCEPT_STATUS_OFFLINE = "offline"; + public static final String ACCEPT_STATUS_BUSY = "busy"; + public static final String ACCEPT_STATUS_REST = "rest"; + public static final String ACCEPT_STATUS_REJECT = "reject"; + public static final String ACCEPT_STATUS_RESTRICT = "restrict"; + public static final String ACCEPT_STATUS_OTHER = "other"; + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentController.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentController.java index ac9ee74b96..2730a31a88 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentController.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:06:30 + * @LastEditTime: 2024-04-15 13:16:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -59,7 +59,12 @@ public class AgentController { @PostMapping("/create") public ResponseEntity create(@RequestBody AgentRequest agentRequest) { - return ResponseEntity.ok(agentService.create(agentRequest)); + Agent agent = agentService.create(agentRequest); + if (agent == null) { + return ResponseEntity.ok(JsonResult.error("department not exist")); + } + + return ResponseEntity.ok(JsonResult.success(agentService.convertToAgentResponse(agent))); } /** diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentListener.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentListener.java new file mode 100644 index 0000000000..325f42216c --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentListener.java @@ -0,0 +1,71 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 09:30:30 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 08:39:50 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.agent; + +import org.springframework.stereotype.Component; + +import com.bytedesk.core.topic.TopicService; +import com.bytedesk.core.utils.ApplicationContextHolder; + +import jakarta.persistence.PostPersist; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class AgentListener { + + // 无法注入bean,否则报错 + // private final TopicService topicService; + + // @PrePersist + // public void prePersist(Agent agent) { + // log.info("prePersist {}", agent.getUid()); + // } + + @PostPersist + public void postPersist(Agent agent) { + log.info("postPersist {}", agent.getUid()); + // topicService.create(agent.getUid(), agent.getUser().getUid()); + // 这里可以记录日志、发送通知等 + // create agent topic + TopicService topicService = ApplicationContextHolder.getBean(TopicService.class); + // + topicService.create(agent.getUid(), agent.getUser().getUid()); + } + + // @PreUpdate + // public void preUpdate(Agent agent) { + // log.info("preUpdate {}", agent.getUid()); + // } + + // @PostUpdate + // public void postUpdate(Agent agent) { + // log.info("postUpdate {}", agent.getUid()); + // // TODO: 切换agent对应的user时,需要更新topic:从原user缓存中删除,然后添加到新user缓存中 + // // topicService.update(agent.getUid(), agent.getUser().getUid()); + // } + + // @PreRemove + // public void preRemove(Agent agent) { + // log.info("preRemove {}", agent.getUid()); + // } + + // @PostRemove + // public void postRemove(Agent agent) { + // log.info("postRemove {}", agent.getUid()); + // // topicService.deleteByTopicAndUid(agent.getUid(), agent.getUser().getUid()); + // } + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRepository.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRepository.java index 2130b70f70..2396537545 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-06 10:38:09 + * @LastEditTime: 2024-04-22 12:17:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,9 +14,13 @@ */ package com.bytedesk.service.agent; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.security.access.prepost.PreAuthorize; +// import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Repository; import io.swagger.v3.oas.annotations.tags.Tag; @@ -26,8 +30,17 @@ import io.swagger.v3.oas.annotations.tags.Tag; */ @Repository @Tag(name = "agent account info - 客服账号信息") -@PreAuthorize("hasRole('ROLE_ADMIN')") +// @PreAuthorize("hasRole('ROLE_ADMIN')") public interface AgentRepository extends JpaRepository, JpaSpecificationExecutor { - // Page findAll(Pageable pageable); + Optional findByUid(String uid); + + Optional findByEmail(String email); + + Optional findByMobile(String mobile); + + Optional findByUser_Uid(String uid); + + // Page findByOrganization_Oid(String oid, Pageable pageable); + Page findByOrgOid(String oid, Pageable pageable); } diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRequest.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRequest.java index 9f249d885f..69d5f1e274 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 10:16:30 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:41:42 + * @LastEditTime: 2024-04-13 22:29:10 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -18,25 +18,34 @@ import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.utils.BaseRequest; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; @Data +@Builder +@Accessors(chain = true) @EqualsAndHashCode(callSuper = false) public class AgentRequest extends BaseRequest { - private String aid; + private String uid; private String nickname; private String password; + @Builder.Default private String avatar = AvatarConsts.DEFAULT_AGENT_AVATAR_URL; private String mobile; private String email; + @Builder.Default private String description = BdConstants.DEFAULT_AGENT_DESCRIPTION; + // organization oid + private String orgOid; + } diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponse.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponse.java index fbf7f19c9b..61122add8a 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 10:17:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 14:49:30 + * @LastEditTime: 2024-04-16 17:07:56 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -31,7 +31,9 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) public class AgentResponse extends BaseResponse { - private String aid; + private static final long serialVersionUID = 94072119L; + + private String uid; private String nickname; @@ -43,5 +45,11 @@ public class AgentResponse extends BaseResponse { private String description; + private String acceptStatus; + + private boolean connected; + private Integer maxThreadCount; + + private String welcomeTip; } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttRetainMessage.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponseSimple.java old mode 100755 new mode 100644 similarity index 66% rename from modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttRetainMessage.java rename to modules/service/src/main/java/com/bytedesk/service/agent/AgentResponseSimple.java index 49ea89e98e..176f8be87c --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttRetainMessage.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentResponseSimple.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 + * @Date: 2024-02-06 10:17:01 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:41:38 + * @LastEditTime: 2024-04-16 17:07:43 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,29 +12,30 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.socket.mqtt.model; +package com.bytedesk.service.agent; -import java.io.Serializable; +import com.bytedesk.core.utils.BaseResponse; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -/** - * Retain标志消息存储 - */ @Data +@Builder @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor -public class MqttRetainMessage implements Serializable { +@EqualsAndHashCode(callSuper = true) +public class AgentResponseSimple extends BaseResponse { - private static final long serialVersionUID = -7548204047370972779L; + private static final long serialVersionUID = 1219497968L; - private String topic; + private String uid; - private byte[] messageBytes; + private String nickname; - private int mqttQoS; + private String avatar; } diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentService.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentService.java index 06ab76bdb7..59cbb013a3 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentService.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:50:31 + * @LastEditTime: 2024-04-25 15:21:19 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,21 +17,25 @@ package com.bytedesk.service.agent; import java.util.Optional; import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.cache.annotation.CachePut; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import com.bytedesk.core.constant.TypeConsts; +import com.bytedesk.core.config.BytedeskProperties; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; -import com.bytedesk.team.department.Department; -import com.bytedesk.team.department.DepartmentRepository; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.team.organization.Organization; +import com.bytedesk.team.organization.OrganizationService; import lombok.AllArgsConstructor; +// import lombok.extern.slf4j.Slf4j; // @Slf4j @Service @@ -40,24 +44,33 @@ public class AgentService { private final AgentRepository agentRepository; - private final DepartmentRepository departmentRepository; - private final ModelMapper modelMapper; private final UserService userService; - public Page query(AgentRequest pageParam) { + private final UidUtils uidUtils; - Pageable pageable = PageRequest.of(pageParam.getPageNumber(), - pageParam.getPageSize(), Sort.Direction.DESC, + // private final TopicService topicService; + + // private final AuthService authService; + + private final BytedeskProperties properties; + + private final OrganizationService organizationService; + + public Page query(AgentRequest agentRequest) { + + Pageable pageable = PageRequest.of(agentRequest.getPageNumber(), + agentRequest.getPageSize(), Sort.Direction.DESC, "id"); - Page agentPage = agentRepository.findAll(pageable); + Page agentPage = agentRepository.findByOrgOid(agentRequest.getOrgOid(), pageable); return agentPage.map(this::convertToAgentResponse); } - - public JsonResult create(AgentRequest agentRequest) { + + @Transactional + public Agent create(AgentRequest agentRequest) { // // if (userService.existsByMobile(agentRequest.getMobile())) { // return JsonResult.error("mobile already exist"); @@ -67,15 +80,10 @@ public class AgentService { // } // Agent agent = modelMapper.map(agentRequest, Agent.class); - agent.setAid(Utils.getUid()); - // - Optional depOptional = departmentRepository.findByName(TypeConsts.DEPT_CUSTOMER_SERVICE); - if (!depOptional.isPresent()) { - return JsonResult.error("department not exist"); - } + agent.setUid(uidUtils.getCacheSerialUid()); // User user; - Optional userOptional = userService.findByMobile(agentRequest.getMobile()); + Optional userOptional = userService.findByEmail(agentRequest.getEmail()); if (!userOptional.isPresent()) { user = userService.createUser( agentRequest.getNickname(), @@ -83,40 +91,107 @@ public class AgentService { agentRequest.getPassword(), agentRequest.getMobile(), agentRequest.getEmail(), - true, - depOptional.get().getOrganization().getOid() + true, + agentRequest.getOrgOid() ); } else { + // just return user user = userOptional.get(); - // user = userService.updateUser( - // userOptional.get(), - // agentRequest.getPassword(), - // agentRequest.getMobile(), - // agentRequest.getEmail() - // ); } agent.setUser(user); + + agent = save(agent); // - return JsonResult.success(save(agent)); + return agent; } public AgentResponse update(AgentRequest agentRequest) { Agent agent = modelMapper.map(agentRequest, Agent.class); - return save(agent); + agent = save(agent); + + return convertToAgentResponse(agent); } - @SuppressWarnings("null") - public AgentResponse save(Agent agent) { - return convertToAgentResponse(agentRepository.save(agent)); + public void updateConnect(String uid, boolean isConnect) { + Optional agentOptional = findByUser_Uid(uid); + agentOptional.ifPresent(agent -> { + agent.setConnected(isConnect); + save(agent); + }); + } + + + @Cacheable(value = "agent", key = "#uid", unless="#result == null") + public Optional findByUid(String uid) { + return agentRepository.findByUid(uid); + } + + @Cacheable(value = "agent", key = "#mobile", unless="#result == null") + public Optional findByMobile(String mobile) { + return agentRepository.findByMobile(mobile); + } + + @Cacheable(value = "agent", key = "#email", unless="#result == null") + public Optional findByEmail(String email) { + return agentRepository.findByEmail(email); + } + + @Cacheable(value = "agent", key = "#userUid", unless="#result == null") + public Optional findByUser_Uid(String userUid) { + return agentRepository.findByUser_Uid(userUid); } - private AgentResponse convertToAgentResponse(Agent agent) { - return new ModelMapper().map(agent, AgentResponse.class); + @Caching(put = { + @CachePut(value = "agent", key = "#agent.uid"), + @CachePut(value = "agent", key = "#agent.mobile"), + @CachePut(value = "agent", key = "#agent.email"), + }) + public Agent save(Agent agent) { + return agentRepository.save(agent); + } + + public AgentResponse convertToAgentResponse(Agent agent) { + return modelMapper.map(agent, AgentResponse.class); + } + + public AgentResponseSimple convertToAgentResponseSimple(Agent agent) { + return modelMapper.map(agent, AgentResponseSimple.class); } + public void initData() { + + if (agentRepository.count() > 0) { + return; + } + + Optional orgOptional = organizationService.findByName(properties.getCompany()); + if (orgOptional.isPresent()) { + // add agent + AgentRequest agent1Request = AgentRequest.builder() + .nickname("Agent1") + .email("agent1@email.com") + .mobile("18888888008") + .password("123456") + .orgOid(orgOptional.get().getOid()) + .build(); + create(agent1Request); + // + AgentRequest agent2Request = AgentRequest.builder() + .nickname("Agent2") + .email("agent2@email.com") + .mobile("18888888009") + .password("123456") + .orgOid(orgOptional.get().getOid()) + .build(); + create(agent2Request); + + } + + + } } diff --git a/modules/service/src/main/java/com/bytedesk/service/customer/Customer.java b/modules/service/src/main/java/com/bytedesk/service/customer/Customer.java index 13eb53a17b..dedba330b1 100644 --- a/modules/service/src/main/java/com/bytedesk/service/customer/Customer.java +++ b/modules/service/src/main/java/com/bytedesk/service/customer/Customer.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-22 16:52:52 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-22 23:12:17 + * @LastEditTime: 2024-04-26 22:53:21 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,7 +16,8 @@ package com.bytedesk.service.customer; import com.bytedesk.core.utils.AuditModel; -import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -44,7 +45,20 @@ public class Customer extends AuditModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id") private Long id; + private String name; + + + /** + * https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html + */ + @Embedded + Address address; + + @Embeddable + public static class Address { + String zipCode, city, street; + } + } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttPublishMessageService.java b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerNameOnly.java similarity index 71% rename from modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttPublishMessageService.java rename to modules/service/src/main/java/com/bytedesk/service/customer/CustomerNameOnly.java index 401bc80337..517d6d3fdf 100644 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttPublishMessageService.java +++ b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerNameOnly.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 + * @Date: 2024-04-12 12:55:55 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:06:27 + * @LastEditTime: 2024-04-12 15:11:08 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,14 +12,14 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.socket.mqtt.service; - -import org.springframework.stereotype.Service; +package com.bytedesk.service.customer; /** - * @author bytedesk.com on 2019-07-18 + * https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html */ -@Service -public class MqttPublishMessageService { +public interface CustomerNameOnly { + + String getName(); + // Optional getName(); } diff --git a/modules/service/src/main/java/com/bytedesk/service/customer/CustomerRepository.java b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerRepository.java index 63825e498b..3f07f5a1a9 100644 --- a/modules/service/src/main/java/com/bytedesk/service/customer/CustomerRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-22 23:06:25 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-22 23:06:26 + * @LastEditTime: 2024-04-12 15:12:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,16 @@ */ package com.bytedesk.service.customer; -public class CustomerRepository { +import java.util.Collection; +import org.springframework.data.repository.Repository; + +/** + * https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html + */ +public interface CustomerRepository extends Repository{ + + Collection findByName(String name); + // using a dynamic projection parameter + Collection findByName(String name, Class type); } diff --git a/modules/service/src/main/java/com/bytedesk/service/doc/Doc.java b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerSummary.java similarity index 67% rename from modules/service/src/main/java/com/bytedesk/service/doc/Doc.java rename to modules/service/src/main/java/com/bytedesk/service/customer/CustomerSummary.java index ae8b4f8f52..f8bffacbe9 100644 --- a/modules/service/src/main/java/com/bytedesk/service/doc/Doc.java +++ b/modules/service/src/main/java/com/bytedesk/service/customer/CustomerSummary.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-02-22 16:18:37 + * @Date: 2024-04-12 15:06:27 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 16:20:48 + * @LastEditTime: 2024-04-12 15:09:56 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,11 +12,16 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.doc; +package com.bytedesk.service.customer; /** - * 帮助文档 + * https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html */ -public class Doc { +public interface CustomerSummary { + String getName(); + AddressSummary getAddress(); + interface AddressSummary { + String getCity(); + } } diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessage.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessage.java similarity index 97% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessage.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessage.java index 70ed28a733..f16d23ea6b 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessage.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessage.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; import com.bytedesk.core.utils.AuditModel; diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageController.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageController.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageController.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageController.java index 95fe2bf63b..3aceeb2fbc 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageController.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageController.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; public class LeaveMessageController { diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRepository.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRepository.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRepository.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRepository.java index 4dbc84ec8c..07cae3a561 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRepository.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; public class LeaveMessageRepository { diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRequest.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRequest.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRequest.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRequest.java index 141b1fe0f4..b18f3724b4 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageRequest.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; public class LeaveMessageRequest { diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageResponse.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageResponse.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageResponse.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageResponse.java index 67cc2b1ff5..6534461c20 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageResponse.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; public class LeaveMessageResponse { diff --git a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageService.java b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageService.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageService.java rename to modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageService.java index 94d5739e3a..6555f47a95 100644 --- a/modules/service/src/main/java/com/bytedesk/service/leavemsg/LeaveMessageService.java +++ b/modules/service/src/main/java/com/bytedesk/service/leave_msg/LeaveMessageService.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.leavemsg; +package com.bytedesk.service.leave_msg; public class LeaveMessageService { diff --git a/modules/service/src/main/java/com/bytedesk/service/listener/ServiceEventListener.java b/modules/service/src/main/java/com/bytedesk/service/listener/ServiceEventListener.java new file mode 100644 index 0000000000..f0b32598fc --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/listener/ServiceEventListener.java @@ -0,0 +1,53 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-12 17:58:50 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 13:48:59 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.listener; + +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.event.MqttConnectedEvent; +import com.bytedesk.service.agent.AgentService; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@AllArgsConstructor +public class ServiceEventListener { + + private AgentService agentService; + + @EventListener + public void onMqttConnectedEvent(MqttConnectedEvent event) { + String clientId = event.getClientId(); + // 用户clientId格式: uid/client + final String uid = clientId.split("/")[0]; + log.info("Service onMqttConnectedEvent uid {}, clientId {}", uid, clientId); + // + agentService.updateConnect(uid, true); + } + + @EventListener + public void onMqttDisconnectedEvent(MqttConnectedEvent event) { + String clientId = event.getClientId(); + // 用户clientId格式: uid/client + final String uid = clientId.split("/")[0]; + log.info("Service onMqttDisconnectedEvent uid {}, clientId {}", uid, clientId); + // + agentService.updateConnect(uid, false); + } + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheck.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheck.java similarity index 94% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheck.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheck.java index bfcaad2da5..4ac70aba0a 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheck.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheck.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检记录 diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckAppeal.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckAppeal.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckAppeal.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckAppeal.java index 67c823eed6..b3f16b9e6e 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckAppeal.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckAppeal.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检申诉 diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckPlan.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckPlan.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckPlan.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckPlan.java index 82e7e87040..a274f3e409 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckPlan.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckPlan.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检方案 diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSetting.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSetting.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSetting.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSetting.java index e725f3ff02..ad92807890 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSetting.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSetting.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检方案-细分项 diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSettingScore.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSettingScore.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSettingScore.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSettingScore.java index 7aa6f202b3..c6ba19a24d 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckSettingScore.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckSettingScore.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检方案-细分项-评分 diff --git a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckStatistic.java b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckStatistic.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckStatistic.java rename to modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckStatistic.java index e53b1249e6..41a90b26dc 100644 --- a/modules/service/src/main/java/com/bytedesk/service/qualitycheck/QualityCheckStatistic.java +++ b/modules/service/src/main/java/com/bytedesk/service/quality_check/QualityCheckStatistic.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.qualitycheck; +package com.bytedesk.service.quality_check; /** * 质检统计 diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButton.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButton.java similarity index 97% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButton.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButton.java index 91089525b4..c8769b9311 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButton.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButton.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; import com.bytedesk.core.utils.AuditModel; diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonController.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonController.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonController.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonController.java index d1ebf468ad..97be3d270e 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonController.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonController.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; public class QuickButtonController { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRepository.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRepository.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRepository.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRepository.java index be3a3d699b..b40ef811e5 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRepository.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; public class QuickButtonRepository { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRequest.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRequest.java similarity index 94% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRequest.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRequest.java index 77396cba63..c101e24633 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonRequest.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; public class QuickButtonRequest { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonResponse.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonResponse.java similarity index 94% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonResponse.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonResponse.java index f55b0ba833..cee51fe320 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonResponse.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; public class QuickButtonResponse { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonService.java b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonService.java similarity index 94% rename from modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonService.java rename to modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonService.java index b6feaa5205..7c7594ca2c 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickbutton/QuickButtonService.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_button/QuickButtonService.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickbutton; +package com.bytedesk.service.quick_button; public class QuickButtonService { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReply.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReply.java similarity index 97% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReply.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReply.java index 290bcb617c..4df8700d19 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReply.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReply.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; import com.bytedesk.core.utils.AuditModel; diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyController.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyController.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyController.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyController.java index 914a583aa4..091e9fc944 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyController.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyController.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; public class QuickReplyController { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRepository.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRepository.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRepository.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRepository.java index 2394979dbf..c3ab28e852 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRepository.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; public interface QuickReplyRepository { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRequest.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRequest.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRequest.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRequest.java index 64d48a2b7f..395433f09c 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyRequest.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; public class QuickReplyRequest { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyResponse.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyResponse.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyResponse.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyResponse.java index d9545f5df9..a81f939ca7 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyResponse.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; public class QuickReplyResponse { diff --git a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyService.java b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyService.java similarity index 95% rename from modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyService.java rename to modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyService.java index a03acd74ff..71541bddc2 100644 --- a/modules/service/src/main/java/com/bytedesk/service/quickreply/QuickReplyService.java +++ b/modules/service/src/main/java/com/bytedesk/service/quick_reply/QuickReplyService.java @@ -12,7 +12,7 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.service.quickreply; +package com.bytedesk.service.quick_reply; public class QuickReplyService { diff --git a/modules/service/src/main/java/com/bytedesk/service/route/AbstractStrategyRouter.java b/modules/service/src/main/java/com/bytedesk/service/route/AbstractStrategyRouter.java new file mode 100644 index 0000000000..13f3d14e7d --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/route/AbstractStrategyRouter.java @@ -0,0 +1,113 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-09 14:56:40 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 15:01:57 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.route; + +import java.util.Objects; + +import org.springframework.stereotype.Component; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.Setter; + +/** + * https://mp.weixin.qq.com/s/Wib0Ly45te00HMUnIG-tbg + * https://edu.aliyun.com/course/313614 + * + * 通用的“策略树“框架,通过树形结构实现分发与委托,每层通过指定的参数进行向下分发委托,直到达到最终的执行者。 + * 该框架包含两个类:{@code StrategyHandler} 和 {@code AbstractStrategyRouter} + * 其中:通过实现 {@code AbstractStrategyRouter} 抽象类完成对策略的分发, + * 实现 {@code StrategyHandler} 接口来对策略进行实现。 + * 像是第二层 A、B 这样的节点,既是 Root 节点的策略实现者也是策略A1、A2、B1、B2 的分发者,这样的节点只需要 + * 同时继承 {@code StrategyHandler} 和实现 {@code AbstractStrategyRouter} 接口就可以了。 + * + *

    + *           +---------+
    + *           |  Root   |   ----------- 第 1 层策略入口
    + *           +---------+
    + *            /       \  ------------- 根据入参 P1 进行策略分发
    + *           /         \
    + *     +------+      +------+
    + *     |  A   |      |  B   |  ------- 第 2 层不同策略的实现
    + *     +------+      +------+
    + *       /  \          /  \  --------- 根据入参 P2 进行策略分发
    + *      /    \        /    \
    + *   +---+  +---+  +---+  +---+
    + *   |A1 |  |A2 |  |B1 |  |B2 |  ----- 第 3 层不同策略的实现
    + *   +---+  +---+  +---+  +---+
    + * 
    + * + * @author + * @date + * @see StrategyHandler + */ +@Component +public abstract class AbstractStrategyRouter { + + /** + * 策略映射器,根据指定的入参路由到对应的策略处理者。 + * + * @param 策略的入参类型 + * @param 策略的返回值类型 + */ + public interface StrategyMapper { + /** + * 根据入参获取到对应的策略处理者。可通过 if-else 实现,也可通过 Map 实现。 + * + * @param param 入参 + * @return 策略处理者 + */ + StrategyHandler get(T param); + } + + private StrategyMapper strategyMapper; + + /** + * 类初始化时注册分发策略 Mapper + */ + @PostConstruct + private void abstractInit() { + strategyMapper = registerStrategyMapper(); + Objects.requireNonNull(strategyMapper, "strategyMapper cannot be null"); + } + + @Getter + @Setter + @SuppressWarnings("unchecked") + private StrategyHandler defaultStrategyHandler = StrategyHandler.DEFAULT; + + /** + * 执行策略,框架会自动根据策略分发至下游的 Handler 进行处理 + * + * @param param 入参 + * @return 下游执行者给出的返回值 + */ + public R applyStrategy(T param) { + final StrategyHandler strategyHandler = strategyMapper.get(param); + if (strategyHandler != null) { + return strategyHandler.apply(param); + } + + return defaultStrategyHandler.apply(param); + } + + /** + * 抽象方法,需要子类实现策略的分发逻辑 + * + * @return 分发逻辑 Mapper 对象 + */ + protected abstract StrategyMapper registerStrategyMapper(); + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/route/IRouteService.java b/modules/service/src/main/java/com/bytedesk/service/route/IRouteService.java new file mode 100644 index 0000000000..eebdfba996 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/route/IRouteService.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-08 13:03:35 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 13:08:44 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.route; + +public interface IRouteService { + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/push/EmailService.java b/modules/service/src/main/java/com/bytedesk/service/route/Route.java similarity index 84% rename from modules/core/src/main/java/com/bytedesk/core/push/EmailService.java rename to modules/service/src/main/java/com/bytedesk/service/route/Route.java index 677135250f..7f4126f1f2 100644 --- a/modules/core/src/main/java/com/bytedesk/core/push/EmailService.java +++ b/modules/service/src/main/java/com/bytedesk/service/route/Route.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-03-31 15:30:19 + * @Date: 2024-04-08 13:03:09 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-31 15:30:22 + * @LastEditTime: 2024-04-08 13:03:12 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,8 +12,8 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.push; +package com.bytedesk.service.route; -public class EmailService { +public class Route { } diff --git a/modules/service/src/main/java/com/bytedesk/service/route/RouteFactory.java b/modules/service/src/main/java/com/bytedesk/service/route/RouteFactory.java new file mode 100644 index 0000000000..35d6051abb --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/route/RouteFactory.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-09 14:05:05 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-09 14:05:08 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.route; + +public class RouteFactory { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/route/RouteRepository.java b/modules/service/src/main/java/com/bytedesk/service/route/RouteRepository.java new file mode 100644 index 0000000000..3d85fc3586 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/route/RouteRepository.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-08 13:04:04 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-08 13:04:06 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.route; + +public interface RouteRepository { + +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionService.java b/modules/service/src/main/java/com/bytedesk/service/route/StrategyHandler.java similarity index 66% rename from modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionService.java rename to modules/service/src/main/java/com/bytedesk/service/route/StrategyHandler.java index 7842410b18..476ebdffc1 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionService.java +++ b/modules/service/src/main/java/com/bytedesk/service/route/StrategyHandler.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:24 + * @Date: 2024-04-09 15:01:26 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:03:51 + * @LastEditTime: 2024-04-09 15:01:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,24 +12,24 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.rbac.action; +package com.bytedesk.service.route; -import lombok.AllArgsConstructor; - -import org.springframework.stereotype.Service; - -import java.util.List; - -// @Slf4j -@Service -@AllArgsConstructor -public class ActionService { - - private final ActionRepository actionRepository; - - public List findAll() { - return actionRepository.findAll(); - } +/** + * https://mp.weixin.qq.com/s/Wib0Ly45te00HMUnIG-tbg + * + * @author + * @date + */ +public interface StrategyHandler { + @SuppressWarnings("rawtypes") + StrategyHandler DEFAULT = t -> null; + /** + * apply strategy + * + * @param param + * @return + */ + R apply(T param); } diff --git a/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLog.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLog.java new file mode 100644 index 0000000000..29bb66f817 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLog.java @@ -0,0 +1,115 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-09 16:34:13 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 21:38:50 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.thread_log; + +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import com.bytedesk.core.constant.BdConstants; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; +import com.bytedesk.core.rbac.user.User; +import com.bytedesk.core.utils.AuditModel; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * only for customer service thread history, 只记录客服对话历史,不记录同事和群组对话历史 + * visitor service history, used for admin backend query + * thread table in core module is used for agent client query to avoid duplication + */ +@Entity +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "service_thread_log") +public class ThreadLog extends AuditModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(unique = true, nullable = false) + private String tid; + + /** + * used to push message + * topic format: + * workgroup_wid + '/' + visitor_vid + * agent_aid + '/' + visitor_vid + * such as: wid/vid or aid/vid + */ + private String topic; + + @Builder.Default + private String content = BdConstants.EMPTY_STRING; + + @Builder.Default + private Integer unreadCount = 0; + + /** + * @{ThreadTypeConsts} + */ + @Builder.Default + @Column(name = "by_type") + private String type = ThreadTypeConsts.WORKGROUP; + + // closed/open + @Builder.Default + private String status = StatusConsts.THREAD_STATUS_OPEN; + + private String client; + + // @Lob + @Builder.Default + @Column(columnDefinition = "json") + // 用于兼容postgreSQL,否则会报错,[ERROR: column "extra" is of type json but expression is of type character varying + @JdbcTypeCode(SqlTypes.JSON) + private String extra = BdConstants.EMPTY_JSON_STRING; + + // + // h2 db 不能使用 user, 所以重定义为 by_user + @Builder.Default + @Column(name = "by_user", columnDefinition = "json") + @JdbcTypeCode(SqlTypes.JSON) + private String user = BdConstants.EMPTY_JSON_STRING; + + /** belongs to user */ + @JsonIgnore + @ManyToOne(fetch = FetchType.LAZY) + private User owner; + + + /** belong to org */ + private String orgOid; + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogController.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogController.java new file mode 100644 index 0000000000..89050837d3 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogController.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 10:48:52 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 10:48:54 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.thread_log; + +public class ThreadLogController { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRepository.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRepository.java new file mode 100644 index 0000000000..f2903c7a64 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRepository.java @@ -0,0 +1,26 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 10:48:16 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 16:21:55 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.thread_log; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ThreadLogRepository extends JpaRepository { + + Page findByOrgOid(String orgOid, Pageable pageable); + + Boolean existsByTid(String tid); +} diff --git a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRequest.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRequest.java similarity index 69% rename from modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRequest.java rename to modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRequest.java index 9aa185ef8e..c9257799ab 100644 --- a/modules/core/src/main/java/com/bytedesk/core/rbac/action/ActionRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogRequest.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-02-26 11:28:15 + * @Date: 2024-04-18 10:48:52 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 14:03:29 + * @LastEditTime: 2024-04-22 16:19:53 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,22 +12,23 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.rbac.action; +package com.bytedesk.service.thread_log; import com.bytedesk.core.utils.BaseRequest; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; @Data -@EqualsAndHashCode(callSuper = true) -public class ActionRequest extends BaseRequest { +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class ThreadLogRequest extends BaseRequest { + + - private String aid; - - private String name; - - private String value; - - private String description; + // organization oid + private String orgOid; } diff --git a/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogResponse.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogResponse.java new file mode 100644 index 0000000000..97c73534f6 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogResponse.java @@ -0,0 +1,55 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 10:49:12 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 16:24:06 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.thread_log; + +import java.util.Date; + +import com.bytedesk.core.rbac.user.UserResponseSimple; +import com.bytedesk.core.utils.BaseResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + + +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class ThreadLogResponse extends BaseResponse { + + private static final long serialVersionUID = 7910814181L; + + private String tid; + + private String topic; + + private String content; + + private Integer unreadCount; + + private String type; + + private String extra; + + private Date createdAt; + + private UserResponseSimple user; +} diff --git a/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogService.java b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogService.java new file mode 100644 index 0000000000..7cb019b219 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/thread_log/ThreadLogService.java @@ -0,0 +1,125 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 10:47:38 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-25 20:43:01 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.thread_log; + +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.modelmapper.ModelMapper; +import org.springframework.data.domain.Sort; +import org.springframework.scheduling.annotation.Async; +import org.springframework.context.event.EventListener; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.event.ThreadCreateEvent; +import com.bytedesk.core.event.ThreadUpdateEvent; +import com.bytedesk.core.thread.Thread; +import com.bytedesk.core.thread.ThreadService; +import com.bytedesk.service.visitor.VisitorExtra; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class ThreadLogService { + + private final ThreadLogRepository threadLogRepository; + + private final ModelMapper modelMapper; + + private final ThreadService threadService; + + public Page query(ThreadLogRequest threadLogRequest) { + + Pageable pageable = PageRequest.of(threadLogRequest.getPageNumber(), + threadLogRequest.getPageSize(), Sort.Direction.DESC, + "updatedAt"); + + Page threadLogPage = threadLogRepository.findByOrgOid(threadLogRequest.getOrgOid(), pageable); + + return threadLogPage.map(this::convertThreadLogResponse); + } + + @Async + public ThreadLog create(Thread thread) { + + if (threadLogRepository.existsByTid(thread.getTid())) { + return null; + } + + ThreadLog threadLog = modelMapper.map(thread, ThreadLog.class); + + return save(threadLog); + } + + /** + * + * TODO: 座席端25分钟不回复则自动断开,推送满意度 + * TODO: 客户端2分钟没有回复坐席则自动推送1分钟计时提醒: + * 温馨提示:您已经有2分钟未有操作了,如再有1分钟未有操作,系统将自动结束本次对话,感谢您的支持与谅解 + * 如果1分钟之内无回复,则推送满意度: + */ + // TODO: 频繁查库,待优化 + @Async + public void autoCloseThread() { + // List threads = threadService.findByExtraClosed(); + List threads = threadService.findStatusOpen(); + // log.info("autoCloseThread size {}", threads.size()); + threads.forEach(thread -> { + // + VisitorExtra extra = JSON.parseObject(thread.getExtra(), VisitorExtra.class); + // 计算两个日期之间的毫秒差 + long diffInMilliseconds = Math.abs(new Date().getTime() - thread.getUpdatedAt().getTime()); + // 转换为分钟 + long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(diffInMilliseconds); + if (diffInMinutes > extra.getAutoCloseMin()) { + // 关闭线程 + threadService.autoClose(thread); + } else { + // log.debug("autoCloseThread: {}, min: {}", thread.getTid(), diffInMinutes); + } + }); + } + + public ThreadLog save(ThreadLog threadLog) { + return threadLogRepository.save(threadLog); + } + + public ThreadLogResponse convertThreadLogResponse(ThreadLog threadLog) { + return modelMapper.map(threadLog, ThreadLogResponse.class); + } + + + @EventListener + public void onThreadCreateEvent(ThreadCreateEvent event) { + Thread thread = event.getThread(); + log.info("onThreadCreateEvent: {}", thread.getTid()); + // create(thread); + } + + @EventListener + public void onThreadUpdateEvent(ThreadUpdateEvent event) { + Thread thread = event.getThread(); + log.info("onThreadUpdateEvent: {}", thread.getTid()); + } + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/Visitor.java b/modules/service/src/main/java/com/bytedesk/service/visitor/Visitor.java index b2dd214e60..c8729b8880 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/Visitor.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/Visitor.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 17:01:14 + * @LastEditTime: 2024-04-12 12:50:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,7 @@ */ package com.bytedesk.service.visitor; +import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.utils.AuditModel; import jakarta.persistence.Column; @@ -22,15 +23,23 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; /** * visitor no need to login, without login can reduce the press of the database */ @Entity @Data +@Builder +@Accessors(chain = true) @EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@NoArgsConstructor @Table(name = "service_visitor") public class Visitor extends AuditModel { @@ -40,30 +49,36 @@ public class Visitor extends AuditModel { /** */ - @Column(unique = true, length = 127) - private String vid; + @Column(name = "uuid", unique = true, nullable = false) + private String uid; /** * developers can set basic visitor info */ private String nickname; - private String avatar; - - private String mobile; - - private String email; + @Builder.Default + private String avatar = AvatarConsts.DEFAULT_VISITOR_AVATAR_URL; + // location info private String ip; private String ipLocation; + // device info private String browser; private String os; private String device; + private String referrer; + + // used for agent notation + private String mobile; + + private String email; + private String note; } \ No newline at end of file diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAspect.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAspect.java index 224807ac24..c43db95840 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAspect.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorAspect.java @@ -1,8 +1,8 @@ /* - * @Visitoror: jackning 270580156@qq.com + * @Author: jackning 270580156@qq.com * @Date: 2024-04-05 14:51:45 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 14:54:48 + * @LastEditTime: 2024-04-17 17:17:12 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,7 +14,6 @@ */ package com.bytedesk.service.visitor; -import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @@ -31,18 +30,37 @@ import lombok.extern.slf4j.Slf4j; public class VisitorAspect { @Pointcut("execution(* com.bytedesk.service.visitor.VisitorController.*(..))") - public void visitorLog() { - log.debug("ActionAspect visitorLog"); - }; + public void visitorLog() {}; @Before("visitorLog()") public void beforeVisitorLog() { - log.debug("ActionAspect beforeVisitorLog"); + // TODO: 拦截骚扰用户 + 被禁ip/ip段 + log.debug("VisitorAspect TODO: 拦截骚扰用户 + 被禁ip/ip段"); } - @After("visitorLog()") - public void afterVisitorLog() { - log.debug("ActionAspect afterVisitorLog"); - // TODO: action log save to db - } + // @After("visitorLog()") + // public void afterVisitorLog() { + // log.debug("VisitorAspect afterVisitorLog"); + // // TODO: action log save to db + // } + + // @Around("visitorLog()") + // public Object aroundVisitorLog(ProceedingJoinPoint joinPoint) throws Throwable { + // log.debug("VisitorAspect aroundVisitorLog before"); + + // long start = System.currentTimeMillis(); + + // // body + // Object result = joinPoint.proceed(); + + // long executionTime = System.currentTimeMillis() - start; + + // log.debug("{} executed in {} ms", joinPoint.getSignature(), executionTime); + + // log.debug("VisitorAspect aroundVisitorLog after"); + + // return result; + // } + + } diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorController.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorController.java index a00fb5fddb..66fd5a50a9 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorController.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 15:25:09 + * @LastEditTime: 2024-04-11 16:50:29 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,17 +21,19 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.bytedesk.core.annotation.ApiRateLimiter; +import com.bytedesk.core.message.MessageResponse; import com.bytedesk.core.utils.BaseRequest; import com.bytedesk.core.utils.JsonResult; - +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; /** * anonymous api, no need to login * http://localhost:9003/swagger-ui/index.html */ -@Slf4j +// @Slf4j @RestController @AllArgsConstructor @RequestMapping("/visitor/api/v1/") @@ -41,41 +43,43 @@ public class VisitorController { /** * init visitor cookies in browser & generate visitor in db + * + * considering multi request from different clients, including ios/android/web, + * apis should not use cookies which is specific to web browsers * http://localhost:9003/visitor/api/v1/init * * @param visitorRequest * @return */ + @ApiRateLimiter(value = 10.0, timeout = 1) @GetMapping("/init") - public ResponseEntity init(VisitorRequest visitorRequest) { + public ResponseEntity init(VisitorRequest visitorRequest, HttpServletRequest request) { + // + Visitor visitor = visitorService.create(visitorRequest, request); + if (visitor == null) { + return ResponseEntity.ok(JsonResult.error("init visitor failed", -401)); + } - log.info("visitor init"); - - return ResponseEntity.ok(JsonResult.success()); + return ResponseEntity.ok(JsonResult.success(visitorService.convertToVisitorResponseSimple(visitor))); } /** - * query + * request thread * * @param visitorRequest * @return */ - @GetMapping("/query") - public ResponseEntity query(VisitorRequest visitorRequest) { - - return ResponseEntity.ok(JsonResult.success()); - } - - /** - * create - * - * @param visitorRequest visitor - * @return json - */ - @PostMapping("/create") - public ResponseEntity create(@RequestBody VisitorRequest visitorRequest) { - - return ResponseEntity.ok(JsonResult.success()); + @GetMapping("/thread") + public ResponseEntity requestThread(VisitorRequest visitorRequest, HttpServletRequest request) { + // TODO: check if visitor is banned + // TODO: check if visitor ip is banned + // + MessageResponse messageResponse = visitorService.createCustomerServiceThread(visitorRequest); + if (messageResponse == null) { + return ResponseEntity.ok(JsonResult.error("sid not exist", -402)); + } + // + return ResponseEntity.ok(JsonResult.success(messageResponse)); } /** diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorExtra.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorExtra.java new file mode 100644 index 0000000000..e9595b01cd --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorExtra.java @@ -0,0 +1,55 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-08 12:03:27 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-23 10:01:27 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.visitor; + +import com.bytedesk.service.agent.AgentResponseSimple; +import com.bytedesk.core.constant.BdConstants; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * used for visitor thread extra info + */ +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@NoArgsConstructor +public class VisitorExtra { + + @Builder.Default + private String welcomeTip = BdConstants.DEFAULT_WORK_GROUP_ACCEPT_TIP; + + // visitor_vid + // private String uid; + private VisitorResponseSimple visitor; + + private AgentResponseSimple agent; + + // whether thread is closed + // @Builder.Default + // private boolean isClosed = false; + + /** auto close time in min - 默认自动关闭时间,单位分钟 */ + @Builder.Default + private Double autoCloseMin = Double.valueOf(25); + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRepository.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRepository.java index 6b44597bda..9c68c49f53 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRepository.java @@ -1,3 +1,17 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:24 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-11 11:38:34 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.service.visitor; import java.util.Optional; @@ -12,5 +26,5 @@ import org.springframework.stereotype.Repository; @Repository public interface VisitorRepository extends JpaRepository, JpaSpecificationExecutor { - Optional findByVid(String vid); + Optional findByUid(String uid); } diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java index 22b67a7ae7..b9a98ff2f7 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-04 17:05:48 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 17:07:22 + * @LastEditTime: 2024-04-16 14:57:23 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,8 @@ */ package com.bytedesk.service.visitor; +import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; import com.bytedesk.core.utils.BaseRequest; import lombok.Data; @@ -23,29 +25,56 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = false) public class VisitorRequest extends BaseRequest { - private String vid; + private String uid; /** * developers can set basic visitor info */ private String nickname; - private String avatar; - - private String mobile; - - private String email; + private String avatar = AvatarConsts.DEFAULT_VISITOR_AVATAR_URL; + // location info private String ip; private String ipLocation; + // device info private String browser; private String os; private String device; + private String referrer; + + // used for agent notation + private String mobile; + + private String email; + private String note; + // from source + private String client; + + // for thread request + // private String type; // use super.type + private String sid; + + + public String formatTopic(String uid) { + return this.sid + "/" + uid; + // return formatType() + "/" + this.sid + "/" + uid; + } + + public String formatType() { + if (type.equals("1")) { + return ThreadTypeConsts.APPOINTED; + } else if (type.equals("2")) { + return ThreadTypeConsts.WORKGROUP; + } else { + return type; + } + } } diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponse.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponse.java index 76823e0f17..a7ee1c757a 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-04 17:05:59 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 17:07:01 + * @LastEditTime: 2024-04-16 17:07:29 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -24,6 +24,10 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; + +/** + * used for agent client + */ @Data @Builder @Accessors(chain = true) @@ -31,8 +35,10 @@ import lombok.experimental.Accessors; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) public class VisitorResponse extends BaseResponse { + + private static final long serialVersionUID = 50026477L; - private String vid; + private String uid; /** * developers can set basic visitor info @@ -41,20 +47,29 @@ public class VisitorResponse extends BaseResponse { private String avatar; - private String mobile; - - private String email; - + // location info private String ip; private String ipLocation; + // device info private String browser; private String os; private String device; + private String referrer; + + // used for agent notation + private String mobile; + + private String email; + private String note; + + // from source + private String client; + } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttWillMessage.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponseSimple.java old mode 100755 new mode 100644 similarity index 62% rename from modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttWillMessage.java rename to modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponseSimple.java index 60a509c37e..635f14beca --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttWillMessage.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorResponseSimple.java @@ -1,8 +1,8 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 + * @Date: 2024-04-04 17:05:59 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:42:01 + * @LastEditTime: 2024-04-16 17:07:17 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -12,33 +12,35 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.socket.mqtt.model; +package com.bytedesk.service.visitor; -import java.io.Serializable; +import com.bytedesk.core.utils.BaseResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; + /** - * PUBLISH重发消息存储 + * used for agent client */ @Data +@Builder @Accessors(chain = true) +@AllArgsConstructor @NoArgsConstructor -public class MqttWillMessage implements Serializable { +@EqualsAndHashCode(callSuper = true) +public class VisitorResponseSimple extends BaseResponse { - private static final long serialVersionUID = -8112511377194421600L; - - private int messageId; - - private String clientId; - - private String willTopic; - - private boolean willRetain; - - private int willQoS; + private static final long serialVersionUID = 6459370944L; - private byte[] willMessageBytes; + private String uid; + + private String nickname; + + private String avatar; } diff --git a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorService.java b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorService.java index 8b5378d3f8..69409283ec 100644 --- a/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorService.java +++ b/modules/service/src/main/java/com/bytedesk/service/visitor/VisitorService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:24 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 17:09:07 + * @LastEditTime: 2024-04-23 16:06:38 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,22 +16,338 @@ package com.bytedesk.service.visitor; import java.util.Optional; +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.cache.annotation.CachePut; import org.springframework.stereotype.Service; +import org.springframework.util.SerializationUtils; +import org.springframework.util.StringUtils; +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.constant.AvatarConsts; +import com.bytedesk.core.constant.ClientConsts; +import com.bytedesk.core.constant.MessageTypeConsts; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; +import com.bytedesk.core.event.BytedeskEventPublisher; +import com.bytedesk.core.ip.IpService; +import com.bytedesk.core.message.Message; +import com.bytedesk.core.message.MessageResponse; +import com.bytedesk.core.message.MessageService; +import com.bytedesk.core.rbac.user.UserResponseSimple; +import com.bytedesk.core.thread.Thread; +import com.bytedesk.core.thread.ThreadService; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.service.agent.Agent; +import com.bytedesk.service.agent.AgentResponseSimple; +import com.bytedesk.service.agent.AgentService; +import com.bytedesk.service.workgroup.Workgroup; +import com.bytedesk.service.workgroup.WorkgroupService; + +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; -// @Slf4j +@Slf4j @Service @AllArgsConstructor public class VisitorService { private final VisitorRepository visitorRepository; - public Optional findByVid(String vid) { - return visitorRepository.findByVid(vid); + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + private final IpService ipService; + + private final ThreadService threadService; + + private final AgentService agentService; + + private final WorkgroupService workgroupService; + + private final MessageService messageService; + + private final BytedeskEventPublisher bytedeskEventPublisher; + + @Cacheable(value = "visitor", key = "#uid", unless="#result == null") + public Optional findByUid(String uid) { + return visitorRepository.findByUid(uid); } + /** + * create visitor record + * + * @param visitorRequest + * @return + */ + public Visitor create(VisitorRequest visitorRequest, HttpServletRequest request) { + + String uid = visitorRequest.getUid(); + log.info("visitor init, uid: {}", uid); + // + Visitor visitor = findByUid(uid).orElse(null); + if (visitor != null) { + return visitor; + } + // + if (!StringUtils.hasText(visitorRequest.getNickname())) { + visitorRequest.setNickname(createNickname(request)); + } + if (!StringUtils.hasText(visitorRequest.getAvatar())) { + visitorRequest.setAvatar(AvatarConsts.DEFAULT_VISITOR_AVATAR_URL); + } + // + String ip = ipService.getIp(request); + if (ip != null) { + visitorRequest.setIp(ip); + visitorRequest.setIpLocation(ipService.getIpLocation(ip)); + } + + visitor = modelMapper.map(visitorRequest, Visitor.class); + if (visitor != null) { + visitor.setUid(uidUtils.getCacheSerialUid()); + return save(visitor); + } + + return null; + } + /** */ + public MessageResponse createCustomerServiceThread(VisitorRequest visitorRequest) { + // + String topic = visitorRequest.formatTopic(visitorRequest.getUid()); + // + Thread thread = getThread(visitorRequest, topic); + // + if (thread == null) { + return null; + } + + // TODO: check is agent is online + + // TODO: check push token, if offline & has token, push offline message + + // + Message lastMessage = findOrCreateThreadMessage(visitorRequest, thread); + // + MessageResponse messageResponse = convertToMessageResponse(lastMessage, thread); + + // notify agent - 通知客服 + notifyAgent(thread, messageResponse); + + return messageResponse; + } + + private Thread getThread(VisitorRequest visitorRequest, String topic) { + if (visitorRequest == null) { + throw new IllegalArgumentException("visitorRequest cannot be null"); + } + + Optional threadOptional = threadService.findByTopic(topic); + Thread thread = threadOptional.orElseGet(() -> { + // + String type = visitorRequest.formatType(); + // + Thread newThread = new Thread(); + newThread.setTid(uidUtils.getCacheSerialUid()); + newThread.setTopic(topic); + newThread.setType(type); + newThread.setClient(visitorRequest.getClient()); + // + VisitorResponseSimple visitor = convertToVisitorResponseSimple(visitorRequest); + newThread.setUser(JSON.toJSONString(visitor)); + // + VisitorExtra extra = new VisitorExtra(); + extra.setVisitor(visitor); + // + if (type.equals(ThreadTypeConsts.APPOINTED)) { + // 一对一 + String aid = visitorRequest.getSid(); + Optional agentOptional = agentService.findByUid(aid); + if (agentOptional.isPresent()) { + // + Agent agent = agentOptional.get(); + extra.setWelcomeTip(agent.getWelcomeTip()); + extra.setAgent(agentService.convertToAgentResponseSimple(agent)); + extra.setAutoCloseMin(agent.getAutoCloseMin()); + // + newThread.setOwner(agent.getUser()); + newThread.setContent(agent.getWelcomeTip()); + newThread.setOrgOid(agent.getOrgOid()); + } else { + log.error("agent aid {} not exist", aid); + return null; + } + } else { + // 技能组 + log.debug("workgroup {}", topic); + String aid = visitorRequest.getSid(); + Optional workgroupOptional = workgroupService.findByWid(aid); + if (workgroupOptional.isPresent()) { + Workgroup workgroup = workgroupOptional.get(); + if (!workgroup.getAgents().isEmpty()) { + // 获取workgroup的第一个agent + // TODO: 根据算法选择一个agent + Agent agent = workgroup.getAgents().iterator().next(); + extra.setWelcomeTip(workgroup.getWelcomeTip()); + extra.setAgent(agentService.convertToAgentResponseSimple(agent)); + extra.setAutoCloseMin(agent.getAutoCloseMin()); + // + newThread.setOwner(agent.getUser()); + newThread.setContent(workgroup.getWelcomeTip()); + newThread.setOrgOid(agent.getOrgOid()); + } else { + log.error("No agents found in workgroup with wid {}", aid); + return null; + } + } else { + log.error("workgroup wid {} not exist", aid); + return null; + } + } + // + newThread.setExtra(JSON.toJSONString(extra)); + return threadService.save(newThread); + }); + // + return thread; + } + private Message findOrCreateThreadMessage(VisitorRequest visitorRequest, Thread thread) { + if (thread == null) { + throw new IllegalArgumentException("Thread cannot be null"); + } + + // if thread is closed, reopen it and then create a new message + if (threadService.isClosed(thread)) { + VisitorExtra extra = JSON.parseObject(thread.getExtra(), VisitorExtra.class); + thread.setContent(extra.getWelcomeTip()); + thread = threadService.reopen(thread); + // + Message newMessage = createDefaultMessageForThread(thread); + return messageService.save(newMessage); + } + + // find the last message + Optional messageOptional = messageService.findByThreadsTidInOrderByCreatedAtDesc(thread.getTid()); + if (messageOptional.isPresent()) { + return messageOptional.get(); + } + + // create new message + Message newMessage = createDefaultMessageForThread(thread); + return messageService.save(newMessage); + } + + private Message createDefaultMessageForThread(Thread thread) { + VisitorExtra extra = JSON.parseObject(thread.getExtra(), VisitorExtra.class); + UserResponseSimple user = convertToUserResponseSimple(extra.getAgent()); + + Message message = Message.builder() + .mid(uidUtils.getCacheSerialUid()) + .type(MessageTypeConsts.NOTIFICATION_THREAD) + .content(extra.getWelcomeTip()) + .status(StatusConsts.MESSAGE_STATUS_READ) + .client(ClientConsts.CLIENT_SYSTEM) + .user(JSON.toJSONString(user)) + .orgOid(thread.getOrgOid()) + .build(); + + message.getThreads().add(thread); + return message; + } + + @Caching(put = { + @CachePut(value = "visitor", key = "#visitor.uid"), + }) + private Visitor save(Visitor visitor) { + return visitorRepository.save(visitor); + } + + public String createNickname(HttpServletRequest request) { + + String location = ipService.getIpLocation(request); + // TODO: 修改昵称后缀数字为从1~递增 + String randomId = uidUtils.getCacheSerialUid().substring(11, 15); + + // location: "国家|区域|省份|城市|ISP" + // location: "中国|0|湖北省|武汉市|联通" + // 0|0|0|内网IP|内网IP + String[] locals = location.split("|"); + // log.info("locals {}", location); + if (locals.length > 2) { + if (locals[2].equals("0")) { + return "LOCAL" + randomId; + } + return locals[2] + randomId; + } + + return "Visitor"; + } + + public void notifyAgent(Thread thread, MessageResponse messageResponse) { + try { + // 克隆MessageResponse对象 + MessageResponse agentMessageResponse = SerializationUtils.clone(messageResponse); + + // 验证并解析extra为VisitorExtra对象 + String extraJson = thread.getExtra(); + if (StringUtils.hasText(extraJson) && JSON.isValid(extraJson)) { + VisitorExtra extra = JSON.parseObject(extraJson, VisitorExtra.class); + + // 验证Visitor对象并转换 + VisitorResponseSimple visitor = extra.getVisitor(); + if (visitor != null) { + // user替换成访客,否则客服端会显示客服自己的头像 + UserResponseSimple user = convertToUserResponseSimple(visitor); + agentMessageResponse.setUser(user); + + // 发布消息事件 + String json = JSON.toJSONString(agentMessageResponse); + bytedeskEventPublisher.publishMessageJsonEvent(json); + } else { + // 处理visitor为空的情况 + } + } else { + // 处理extraJson为空或无效的情况 + } + } catch (Exception e) { + // 处理其他异常 + } + } + + public VisitorResponse convertToVisitorResponse(Visitor visitor) { + return modelMapper.map(visitor, VisitorResponse.class); + } + + public VisitorResponseSimple convertToVisitorResponseSimple(Visitor visitor) { + return modelMapper.map(visitor, VisitorResponseSimple.class); + } + + public VisitorResponseSimple convertToVisitorResponseSimple(VisitorRequest visitorRequest) { + return modelMapper.map(visitorRequest, VisitorResponseSimple.class); + } + + public UserResponseSimple convertToUserResponseSimple(AgentResponseSimple agentResponseSimple) { + return modelMapper.map(agentResponseSimple, UserResponseSimple.class); + } + + public UserResponseSimple convertToUserResponseSimple(VisitorResponseSimple visitorResponseSimple) { + return modelMapper.map(visitorResponseSimple, UserResponseSimple.class); + } + + public MessageResponse convertToMessageResponse(Message lastMessage, Thread thread) { + // + MessageResponse messageResponse = modelMapper.map(lastMessage, MessageResponse.class); + messageResponse.setThread(threadService.convertToThreadResponseSimple(thread)); + // + UserResponseSimple user = JSON.parseObject(lastMessage.getUser(), UserResponseSimple.class); + messageResponse.setUser(user); + + return messageResponse; + } } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/Workgroup.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/Workgroup.java index 9748f09d1a..43d1e83799 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/Workgroup.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/Workgroup.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 22:51:38 + * @LastEditTime: 2024-04-23 16:04:38 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,12 +16,18 @@ package com.bytedesk.service.workgroup; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.HashSet; + +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.BdConstants; import com.bytedesk.core.constant.RouteConsts; import com.bytedesk.core.utils.AuditModel; import com.bytedesk.service.agent.Agent; +import com.bytedesk.service.worktime.Worktime; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; @@ -45,13 +51,12 @@ import lombok.experimental.Accessors; @Table(name = "service_workgroup") public class Workgroup extends AuditModel { + private static final long serialVersionUID = 680083751L; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - /** - * - */ @Column(unique = true, nullable = false) private String wid; @@ -64,48 +69,87 @@ public class Workgroup extends AuditModel { private String description = BdConstants.DEFAULT_WORK_GROUP_DESCRIPTION; /** - * - */ - - - /** - * + * route type */ @Builder.Default private String routeType = RouteConsts.ROUTE_TYPE_ROBIN; /** - * + * 熟客优先 */ @Builder.Default @Column(name = "is_recent") - private Boolean recent = false; + private boolean recent = false; - /** - * - */ @Builder.Default @Column(name = "is_auto_pop") - private Boolean autoPop = false; + private boolean autoPop = false; /** - * + * tips + * TODO: set different tips for different lang */ @Builder.Default - private Boolean showTopTip = false; + private boolean showTopTip = false; @Builder.Default @Column(length = 512) private String topTip = BdConstants.DEFAULT_WORK_GROUP_DEFAULT_TOP_TIP; + @Builder.Default + private String welcomeTip = BdConstants.DEFAULT_WORK_GROUP_WELCOME_TIP; + /** - * + * robot + * 是否默认机器人接待 + */ + @Builder.Default + private boolean defaultRobot = false; + + /** 无客服在线时,是否启用机器人接待 */ + @Builder.Default + private boolean offlineRobot = false; + + /** 非工作时间段,是否启用机器人接待 */ + @Builder.Default + private boolean nonWorktimeRobot = false; + + /** auto close time in min - 默认自动关闭时间,单位分钟 */ + @Builder.Default + private Double autoCloseMin = Double.valueOf(25); + + /** work time */ + @Builder.Default + @OneToMany(fetch = FetchType.LAZY) + private List workTimes = new ArrayList<>(); + + /** + * one wg can have many agents, one agent can belong to many wgs */ @JsonIgnore @Builder.Default - @OneToMany - private List agents = new ArrayList<>(); - + @ManyToMany(fetch = FetchType.LAZY) + private Set agents = new HashSet<>(); + /** 存储下一个待分配的客服等信息 */ + @Builder.Default + @Column(columnDefinition = "json") + // 用于兼容postgreSQL,否则会报错,[ERROR: column "extra" is of type json but expression is of type character varying + @JdbcTypeCode(SqlTypes.JSON) + private String extra = BdConstants.EMPTY_JSON_STRING; + /** + * belong to org + */ + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // private Organization organization; + private String orgOid; + + /** + * belongs to user + */ + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // private User owner; } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupAspect.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupAspect.java index f357232a84..daf4383e30 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupAspect.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupAspect.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-04-05 15:08:10 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-05 15:17:20 + * @LastEditTime: 2024-04-17 17:10:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,33 +14,28 @@ */ package com.bytedesk.service.workgroup; -import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; -import lombok.extern.slf4j.Slf4j; - -@Slf4j +// @Slf4j @Aspect @Component public class WorkgroupAspect { - @Pointcut("execution(* com.bytedesk.service.workgroup.WorkgroupController.*(..))") - public void workgroupLog() { - log.debug("ActionAspect workgroupLog"); - }; + // @Pointcut("execution(* com.bytedesk.service.workgroup.WorkgroupController.*(..))") + // public void workgroupLog() { + // log.debug("ActionAspect workgroupLog"); + // }; - @Before("workgroupLog()") - public void beforeWorkgroupLog() { - log.debug("ActionAspect beforeWorkgroupLog"); - } + // @Before("workgroupLog()") + // public void beforeWorkgroupLog() { + // log.debug("ActionAspect beforeWorkgroupLog"); + // } - @After("workgroupLog()") - public void afterWorkgroupLog() { - log.debug("ActionAspect afterWorkgroupLog"); - // TODO: action log save to db - } + // @After("workgroupLog()") + // public void afterWorkgroupLog() { + // log.debug("ActionAspect afterWorkgroupLog"); + // // TODO: action log save to db + // } } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupController.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupController.java index d03ebaa9de..fa9f5468cf 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupController.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-03 14:51:01 + * @LastEditTime: 2024-04-22 10:37:22 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.bytedesk.core.annotation.ActionLog; import com.bytedesk.core.utils.BaseRequest; import com.bytedesk.core.utils.JsonResult; @@ -43,9 +44,10 @@ public class WorkgroupController { * @param workgroupRequest * @return */ + @ActionLog(title = "workgroup", action = "query") @GetMapping("/query") public ResponseEntity query(WorkgroupRequest workgroupRequest) { - + return ResponseEntity.ok(JsonResult.success(workgroupService.query(workgroupRequest))); } @@ -58,7 +60,12 @@ public class WorkgroupController { @PostMapping("/create") public ResponseEntity create(@RequestBody WorkgroupRequest workgroupRequest) { - return ResponseEntity.ok(workgroupService.create(workgroupRequest)); + Workgroup workgroup = workgroupService.create(workgroupRequest); + if (workgroup == null) { + return ResponseEntity.ok(JsonResult.error("create failed")); + } + + return ResponseEntity.ok(JsonResult.success(workgroupService.convertToWorkgroupResponse(workgroup))); } /** @@ -70,8 +77,12 @@ public class WorkgroupController { @PostMapping("/update") public ResponseEntity update(@RequestBody WorkgroupRequest workgroupRequest) { + Workgroup workgroup = workgroupService.update(workgroupRequest); + if (workgroup == null) { + return ResponseEntity.ok(JsonResult.error("update failed")); + } // - return ResponseEntity.ok(JsonResult.success("update success")); + return ResponseEntity.ok(JsonResult.success(workgroupService.convertToWorkgroupResponse(workgroup))); } /** diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRepository.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRepository.java index 8188302fd2..8aaff63069 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRepository.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-28 22:32:02 + * @LastEditTime: 2024-04-22 12:16:54 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,8 +14,13 @@ */ package com.bytedesk.service.workgroup; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.lang.NonNull; // import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Repository; @@ -31,4 +36,11 @@ public interface WorkgroupRepository extends JpaRepository, Jpa // Page findAll(@NonNull Pageable pageable); + Optional findByWid(@NonNull String wid); + + Optional findByNickname(@NonNull String nickname); + + // Page findByOrganization_Oid(String oid, Pageable pageable); + Page findByOrgOid(String oid, Pageable pageable); + } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRequest.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRequest.java index 478bfdffcb..70e36383b5 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRequest.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 10:17:32 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-03 14:58:09 + * @LastEditTime: 2024-04-22 12:38:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,14 +14,22 @@ */ package com.bytedesk.service.workgroup; +import java.util.ArrayList; +import java.util.List; + import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.BdConstants; +import com.bytedesk.core.constant.RouteConsts; import com.bytedesk.core.utils.BaseRequest; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; @Data +@Builder +@Accessors(chain = true) @EqualsAndHashCode(callSuper = false) public class WorkgroupRequest extends BaseRequest { @@ -29,8 +37,40 @@ public class WorkgroupRequest extends BaseRequest { private String nickname; + @Builder.Default private String avatar = AvatarConsts.DEFAULT_WORK_GROUP_AVATAR_URL; + @Builder.Default private String description = BdConstants.DEFAULT_WORK_GROUP_DESCRIPTION; + @Builder.Default + private String routeType = RouteConsts.ROUTE_TYPE_ROBIN; + + @Builder.Default + private Boolean recent = false; + + @Builder.Default + private Boolean autoPop = false; + + @Builder.Default + private Boolean showTopTip = false; + + @Builder.Default + private String topTip = BdConstants.DEFAULT_WORK_GROUP_DEFAULT_TOP_TIP; + + @Builder.Default + private String welcomeTip = BdConstants.DEFAULT_WORK_GROUP_WELCOME_TIP; + + @Builder.Default + private Boolean defaultRobot = false; + + @Builder.Default + private Double autoCloseMin = Double.valueOf(5); + + // 注意:此处不能命名为agents,因与agent中agents类型不同, 否则会报错 + @Builder.Default + private List agentAids = new ArrayList(); + + // organization oid + private String orgOid; } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupResponse.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupResponse.java index e33c0b00a3..047478e51c 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupResponse.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 10:18:02 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-03 15:16:02 + * @LastEditTime: 2024-04-17 13:54:40 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -34,6 +34,8 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) public class WorkgroupResponse extends BaseResponse { + private static final long serialVersionUID = -5451766294L; + private String wid; private String nickname; @@ -42,34 +44,22 @@ public class WorkgroupResponse extends BaseResponse { private String description; - /** - * - */ - - - - /** - * - */ private String routeType; - /** - * - */ private boolean recent; - /** - * - */ private boolean autoPop; - /** - * - */ private boolean showTopTip; private String topTip; + private String welcomeTip; + + private boolean defaultRobot; + + private Double autoCloseMin; + // private List agents; } diff --git a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupService.java b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupService.java index 8daf68c859..0278dc8251 100644 --- a/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupService.java +++ b/modules/service/src/main/java/com/bytedesk/service/workgroup/WorkgroupService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:19:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-03 14:50:35 + * @LastEditTime: 2024-04-25 15:21:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,19 +14,28 @@ */ package com.bytedesk.service.workgroup; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Optional; import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; -import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; +import com.bytedesk.core.config.BytedeskProperties; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.service.agent.Agent; +import com.bytedesk.service.agent.AgentService; +import com.bytedesk.team.organization.Organization; +import com.bytedesk.team.organization.OrganizationService; import lombok.AllArgsConstructor; +import static java.util.Arrays.asList; // @Slf4j @Service @@ -35,41 +44,141 @@ public class WorkgroupService { private final WorkgroupRepository workgroupRepository; + private final AgentService agentService; + private final ModelMapper modelMapper; - public List findAll() { - return workgroupRepository.findAll(); - } + private final UidUtils uidUtils; - public Page query(WorkgroupRequest pageParam) { + private final BytedeskProperties properties; - Pageable pageable = PageRequest.of(pageParam.getPageNumber(), - pageParam.getPageSize(), Sort.Direction.DESC, + private final OrganizationService organizationService; + + public Page query(WorkgroupRequest workgroupRequest) { + + Pageable pageable = PageRequest.of(workgroupRequest.getPageNumber(), + workgroupRequest.getPageSize(), Sort.Direction.DESC, "id"); - Page workgroupPage = workgroupRepository.findAll(pageable); + Page workgroupPage = workgroupRepository.findByOrgOid(workgroupRequest.getOrgOid(), + pageable); return workgroupPage.map(this::convertToWorkgroupResponse); } - - public JsonResult create(WorkgroupRequest workgroupRequest) { - + public Workgroup create(WorkgroupRequest workgroupRequest) { + // Workgroup workgroup = modelMapper.map(workgroupRequest, Workgroup.class); - workgroup.setWid(Utils.getUid()); - - return JsonResult.success(save(workgroup)); + workgroup.setWid(uidUtils.getCacheSerialUid()); + // + Iterator iterator = workgroupRequest.getAgentAids().iterator(); + while (iterator.hasNext()) { + String agentAid = iterator.next(); + Optional agentOptional = agentService.findByUid(agentAid); + if (agentOptional.isPresent()) { + Agent agentEntity = agentOptional.get(); + workgroup.getAgents().add(agentEntity); + } else { + return null; + } + } + // + return save(workgroup); } + Workgroup update(WorkgroupRequest workgroupRequest) { - @SuppressWarnings("null") - private WorkgroupResponse save(Workgroup workgroup) { - return convertToWorkgroupResponse(workgroupRepository.save(workgroup)); + Optional workgroupOptional = findByWid(workgroupRequest.getWid()); + if (!workgroupOptional.isPresent()) { + return null; + } + // + Workgroup workgroup = workgroupOptional.get(); + workgroup = modelMapper.map(workgroupRequest, Workgroup.class); + // workgroupOptional.get().setNickname(workgroupRequest.getNickname()); + // workgroupOptional.get().setAvatar(workgroupRequest.getAvatar()); + // workgroupOptional.get().setDescription(workgroupRequest.getDescription()); + // workgroupOptional.get().setRouteType(workgroupRequest.getRouteType()); + // workgroupOptional.get().setRecent(workgroupRequest.getRecent()); + // workgroupOptional.get().setAutoPop(workgroupRequest.getAutoPop()); + // + // + Iterator iterator = workgroupRequest.getAgentAids().iterator(); + while (iterator.hasNext()) { + String agentAid = iterator.next(); + Optional agentOptional = agentService.findByUid(agentAid); + if (agentOptional.isPresent()) { + Agent agentEntity = agentOptional.get(); + workgroup.getAgents().add(agentEntity); + } else { + return null; + } + } + // + return save(workgroup); + } + + + + @Cacheable(value = "workgroup", key = "#wid", unless="#result == null") + public Optional findByWid(String wid) { + return workgroupRepository.findByWid(wid); } - private WorkgroupResponse convertToWorkgroupResponse(Workgroup workgroup) { + @Cacheable(value = "workgroup", key = "#nickname", unless="#result == null") + public Optional findByNickname(String nickname) { + return workgroupRepository.findByNickname(nickname); + } + + // @SuppressWarnings("null") + private Workgroup save(Workgroup workgroup) { + return workgroupRepository.save(workgroup); + } + + public WorkgroupResponse convertToWorkgroupResponse(Workgroup workgroup) { return modelMapper.map(workgroup, WorkgroupResponse.class); } + public void initData() { + + if (workgroupRepository.count() > 0) { + return; + } + + Optional orgOptional = organizationService.findByName(properties.getCompany()); + if (orgOptional.isPresent()) { + + List agents = new ArrayList<>(); + Optional agent1Optional = agentService.findByMobile("18888888008"); + agent1Optional.ifPresent(agent -> { + agents.add(agent.getUid()); + }); + Optional agent2Optional = agentService.findByMobile("18888888009"); + agent2Optional.ifPresent(agent -> { + agents.add(agent.getUid()); + }); + + // add workgroups + WorkgroupRequest workgroup1Request = WorkgroupRequest.builder() + .nickname("客服组1") + .agentAids(agents) + .orgOid(orgOptional.get().getOid()) + .build(); + create(workgroup1Request); + + // + WorkgroupRequest workgroup2Request = WorkgroupRequest.builder() + .nickname("客服组2") + .agentAids(asList(agent1Optional.get().getUid())) + .orgOid(orgOptional.get().getOid()) + .build(); + create(workgroup2Request); + + } + + + + } + } diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/Worktime.java b/modules/service/src/main/java/com/bytedesk/service/worktime/Worktime.java new file mode 100644 index 0000000000..22a8035263 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/Worktime.java @@ -0,0 +1,114 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:43:29 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:50:05 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import com.bytedesk.core.utils.AuditModel; +import com.fasterxml.jackson.annotation.JsonFormat; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * + */ +@Entity +@Data +@Builder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "service_worktime") +public class Worktime extends AuditModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + /** + * + */ + @Column(name = "start_time") + @JsonFormat(pattern = "HH:mm:ss") + @Temporal(TemporalType.TIME) + private Date startTime; + + /** + * + */ + @Column(name = "end_time") + @JsonFormat(pattern = "HH:mm:ss") + @Temporal(TemporalType.TIME) + private Date endTime; + + /** + * 是否工作时间 + * + * @return 是否工作时间 + */ + public boolean isWorkTime() { + + SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); + String timeString = formatter.format(new Date()); + + Date now = null; + try { + now = formatter.parse(timeString); + } catch (ParseException e) { + e.printStackTrace(); + } + + Calendar calendarNow = Calendar.getInstance(); + calendarNow.setTime(now); + calendarNow.set(0, 0, 0); + + Calendar calendarStart = Calendar.getInstance(); + calendarStart.setTime(startTime); + calendarStart.set(0, 0, 0); + + Calendar calendarEnd = Calendar.getInstance(); + calendarEnd.setTime(endTime); + calendarEnd.set(0, 0, 0); + + boolean result1 = calendarNow.getTime().after(calendarStart.getTime()); + boolean result2 = calendarNow.getTime().before(calendarEnd.getTime()); + + // logger.info("startTime {}, nowTime {}, endTime {}, result1 {}, result2 {}", + // calendarStart.getTime().toString(), calendarNow.getTime().toString(), + // calendarEnd.getTime().toString(), + // result1 ? "before true" : "before false", + // result2 ? "end true" : "end false"); + + return result1 && result2; + } + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeController.java b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeController.java new file mode 100644 index 0000000000..57019062b0 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeController.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:46:55 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:46:57 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +public class WorktimeController { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRepository.java b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRepository.java new file mode 100644 index 0000000000..caa07764d9 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRepository.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:46:25 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:46:26 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +public interface WorktimeRepository { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRequest.java b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRequest.java new file mode 100644 index 0000000000..2ea3b9b8d3 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeRequest.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:46:34 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:46:36 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +public class WorktimeRequest { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeResponse.java b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeResponse.java new file mode 100644 index 0000000000..10e2f3691f --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeResponse.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:46:44 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:46:47 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +public class WorktimeResponse { + +} diff --git a/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeService.java b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeService.java new file mode 100644 index 0000000000..cba2fd2800 --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/worktime/WorktimeService.java @@ -0,0 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-18 14:46:05 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-18 14:46:08 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.worktime; + +public class WorktimeService { + +} diff --git a/modules/social/.DS_Store b/modules/social/.DS_Store index 5bb368debb..f3b5d1376b 100644 Binary files a/modules/social/.DS_Store and b/modules/social/.DS_Store differ diff --git a/modules/social/pom.xml b/modules/social/pom.xml index 607156d5e0..27067172fe 100644 --- a/modules/social/pom.xml +++ b/modules/social/pom.xml @@ -11,7 +11,7 @@ 0.0.1-SNAPSHOT - weiyu-social + im-social social Demo project for Spring Boot @@ -20,7 +20,7 @@ com.bytedesk - weiyu-core + im-core ${im.version} provided @@ -29,10 +29,10 @@ - + diff --git a/modules/socket/.DS_Store b/modules/socket/.DS_Store index 676a195d39..a6814fed60 100644 Binary files a/modules/socket/.DS_Store and b/modules/socket/.DS_Store differ diff --git a/modules/socket/build.gradle b/modules/socket/build.gradle index 6d550776c9..4148be3c74 100644 --- a/modules/socket/build.gradle +++ b/modules/socket/build.gradle @@ -48,8 +48,6 @@ dependencies { implementation 'io.netty:netty-codec-http:4.1.105.Final' - // https://mvnrepository.com/artifact/cn.hutool/hutool-crypto - implementation 'cn.hutool:hutool-crypto:5.8.25' // https://mvnrepository.com/artifact/com.google.guava/guava // implementation 'com.google.guava:guava:33.0.0-jre' // https://mvnrepository.com/artifact/com.google.code.gson/gson diff --git a/modules/socket/mqtt-v3.1.1-os.pdf b/modules/socket/mqtt-v3.1.1-os.pdf new file mode 100644 index 0000000000..03a935fddc Binary files /dev/null and b/modules/socket/mqtt-v3.1.1-os.pdf differ diff --git a/modules/socket/mqtt-v5.0.pdf b/modules/socket/mqtt-v5.0.pdf new file mode 100644 index 0000000000..fa54ff87ce Binary files /dev/null and b/modules/socket/mqtt-v5.0.pdf differ diff --git a/modules/socket/pom.xml b/modules/socket/pom.xml index 551923b3b4..ae1700bc79 100644 --- a/modules/socket/pom.xml +++ b/modules/socket/pom.xml @@ -11,7 +11,7 @@ 0.0.1-SNAPSHOT - weiyu-socket + im-socket socket Demo project for Spring Boot @@ -47,7 +47,7 @@ com.bytedesk - weiyu-core + im-core ${im.version} provided @@ -62,14 +62,6 @@ provided - - - cn.hutool - hutool-crypto - 5.8.25 - provided - - com.google.code.gson diff --git a/modules/socket/readme.md b/modules/socket/readme.md index 2761d253d7..b7f0f7f1cd 100644 --- a/modules/socket/readme.md +++ b/modules/socket/readme.md @@ -21,6 +21,7 @@ gradle generateProto ## links - [mqtt](https://mqtt.org/mqtt-specification/) +- [mqtt-v3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html) - [netty](https://netty.io/wiki/user-guide-for-4.x.html) - [netty mqtt](https://netty.io/4.1/api/io/netty/handler/codec/mqtt/package-summary.html) - [netty mqtt github](https://github.com/netty/netty/tree/4.1/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt) diff --git a/modules/socket/src/.DS_Store b/modules/socket/src/.DS_Store index aa3e1a98c4..ba6efd601d 100644 Binary files a/modules/socket/src/.DS_Store and b/modules/socket/src/.DS_Store differ diff --git a/modules/social/src/.DS_Store b/modules/socket/src/main/.DS_Store similarity index 88% rename from modules/social/src/.DS_Store rename to modules/socket/src/main/.DS_Store index d6db2f8f99..3fe7f8ac97 100644 Binary files a/modules/social/src/.DS_Store and b/modules/socket/src/main/.DS_Store differ diff --git a/modules/oa/src/.DS_Store b/modules/socket/src/main/java/com/bytedesk/.DS_Store similarity index 88% rename from modules/oa/src/.DS_Store rename to modules/socket/src/main/java/com/bytedesk/.DS_Store index 6e3e569075..a9ef03cb4b 100644 Binary files a/modules/oa/src/.DS_Store and b/modules/socket/src/main/java/com/bytedesk/.DS_Store differ diff --git a/modules/socket/src/main/java/com/bytedesk/socket/controller/MessageRestController.java b/modules/socket/src/main/java/com/bytedesk/socket/controller/MessageRestController.java new file mode 100644 index 0000000000..4f14091d07 --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/controller/MessageRestController.java @@ -0,0 +1,59 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-27 16:41:32 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 16:41:34 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.controller; + +import java.util.Map; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.bytedesk.core.utils.JsonResult; +import com.bytedesk.socket.stomp.service.StompMqService; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * + * http://localhost:9003/swagger-ui/index.html + */ +@Slf4j +@RestController +@AllArgsConstructor +@RequestMapping("/api/v1/message/rest") +public class MessageRestController { + + private final StompMqService stompMqService; + + /** + * send offline message + * + * @param map map + * @return json + */ + @PostMapping("/send") + public ResponseEntity sendOfflineMessage(@RequestBody Map map) { + + String json = (String) map.get("json"); + log.debug("json {}", json); + stompMqService.sendMessageToMq(json); + // + return ResponseEntity.ok(JsonResult.success(json)); + } + +} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeNotWildcardCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeNotWildcardCache.java new file mode 100755 index 0000000000..cec994ca18 --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeNotWildcardCache.java @@ -0,0 +1,99 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 09:38:26 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.mqtt.cache; + +// import com.bytedesk.socket.mqtt.model.MqttSubscribe; +// import com.bytedesk.socket.mqtt.util.MqttConsts; +// // import org.springframework.data.redis.core.RedisTemplate; +// // import org.springframework.data.redis.core.StringRedisTemplate; +// import org.springframework.stereotype.Service; + +// import java.util.*; +// import java.util.concurrent.ConcurrentHashMap; + +// /** +// * @author jackning +// */ +// @Service +// public class MqttSubscribeNotWildcardCache { + +// public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "subnotwildcard:"; + +// public static final String CACHE_CLIENT_PRE = MqttConsts.MQTT_PREFIX + "client:"; + +// // @Autowired +// // private StringRedisTemplate stringRedisTemplate; + +// // @Autowired +// // private RedisTemplate redisTemplate; + +// public void put(String topic, String clientId, MqttSubscribe mqttSubscribe) { +// // redisTemplate.opsForHash().put(CACHE_PRE + topic, clientId, mqttSubscribe); +// // stringRedisTemplate.opsForSet().add(CACHE_CLIENT_PRE + clientId, topic); +// } + +// public MqttSubscribe get(String topic, String clientId) { +// // return (MqttSubscribe) redisTemplate.opsForHash().get(CACHE_PRE + topic, clientId); +// return null; +// } + +// public boolean containsKey(String topic, String clientId) { +// // return redisTemplate.opsForHash().hasKey(CACHE_PRE + topic, clientId); +// return false; +// } + +// public void remove(String topic, String clientId) { +// // stringRedisTemplate.opsForSet().remove(CACHE_CLIENT_PRE + clientId, topic); +// // redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); +// } + +// public void removeForClient(String clientId) { +// // for (String topic : stringRedisTemplate.opsForSet().members(CACHE_CLIENT_PRE + clientId)) { +// // redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); +// // } +// // stringRedisTemplate.delete(CACHE_CLIENT_PRE + clientId); +// } + +// public Map> all() { +// Map> map = new HashMap<>(); +// // Set set = redisTemplate.keys(CACHE_PRE + "*"); +// // if (set != null && !set.isEmpty()) { +// // set.forEach( +// // entry -> { +// // ConcurrentHashMap map1 = new ConcurrentHashMap<>(); +// // Map map2 = redisTemplate.opsForHash().entries(entry); +// // if (map2 != null && !map2.isEmpty()) { +// // map2.forEach((k, v) -> { +// // map1.put((String) k, (MqttSubscribe) v); +// // }); +// // map.put(entry.substring(CACHE_PRE.length()), map1); +// // } +// // }); +// // } +// return map; +// } + +// public List all(String topic) { +// List list = new ArrayList<>(); +// // Map map = redisTemplate.opsForHash().entries(CACHE_PRE + topic); +// // if (map != null && !map.isEmpty()) { +// // map.forEach((k, v) -> { +// // list.add((MqttSubscribe) v); +// // }); +// // } +// return list; +// } + +// } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeWildcardCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeWildcardCache.java new file mode 100755 index 0000000000..ad95ca2c4d --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/cache/MqttSubscribeWildcardCache.java @@ -0,0 +1,102 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 09:38:32 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.mqtt.cache; + +// import com.bytedesk.socket.mqtt.model.MqttSubscribe; +// import com.bytedesk.socket.mqtt.util.MqttConsts; +// // import org.springframework.data.redis.core.RedisTemplate; +// // import org.springframework.data.redis.core.StringRedisTemplate; +// import org.springframework.stereotype.Service; + +// import java.util.*; +// import java.util.concurrent.ConcurrentHashMap; + +// /** +// * topic通配符说明: +// * 单层通配符"+":只能匹配一层主题。例如,"aaa/+"可以匹配"aaa/bbb",但不能匹配"aaa/bbb/ccc"。 +// * 多层通配符"#":可以匹配多层主题。例如,"aaa/#"不但可以匹配"aaa/bbb",还可以匹配"aaa/bbb/ccc/ddd"。它必须作为主题的最后一个级别使用,并且只能出现在最后 +// * +// * @author jackning +// */ +// @Service +// public class MqttSubscribeWildcardCache { + +// public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "subwildcard:"; + +// public static final String CACHE_CLIENT_PRE = MqttConsts.MQTT_PREFIX + "client:"; + +// // @Autowired +// // private StringRedisTemplate stringRedisTemplate; + +// // @Autowired +// // private RedisTemplate redisTemplate; + +// public void put(String topic, String clientId, MqttSubscribe subscribeStore) { +// // redisTemplate.opsForHash().put(CACHE_PRE + topic, clientId, subscribeStore); +// // stringRedisTemplate.opsForSet().add(CACHE_CLIENT_PRE + clientId, topic); +// } + +// public MqttSubscribe get(String topic, String clientId) { +// // return (MqttSubscribe) redisTemplate.opsForHash().get(CACHE_PRE + topic, clientId); +// return null; +// } + +// public boolean containsKey(String topic, String clientId) { +// // return redisTemplate.opsForHash().hasKey(CACHE_PRE + topic, clientId); +// return false; +// } + +// public void remove(String topic, String clientId) { +// // stringRedisTemplate.opsForSet().remove(CACHE_CLIENT_PRE + clientId, topic); +// // redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); +// } + +// public void removeForClient(String clientId) { +// // for (String topic : stringRedisTemplate.opsForSet().members(CACHE_CLIENT_PRE + clientId)) { +// // redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); +// // } +// // stringRedisTemplate.delete(CACHE_CLIENT_PRE + clientId); +// } + +// public Map> all() { +// Map> map = new HashMap<>(); +// // Set set = redisTemplate.keys(CACHE_PRE + "*"); +// // if (set != null && !set.isEmpty()) { +// // set.forEach( +// // entry -> { +// // ConcurrentHashMap map1 = new ConcurrentHashMap<>(); +// // Map map2 = redisTemplate.opsForHash().entries(entry); +// // if (map2 != null && !map2.isEmpty()) { +// // map2.forEach((k, v) -> { +// // map1.put((String) k, (MqttSubscribe) v); +// // }); +// // map.put(entry.substring(CACHE_PRE.length()), map1); +// // } +// // }); +// // } +// return map; +// } + +// public List all(String topic) { +// List list = new ArrayList<>(); +// // Map map = redisTemplate.opsForHash().entries(CACHE_PRE + topic); +// // if (map != null && !map.isEmpty()) { +// // map.forEach((k, v) -> { +// // list.add((MqttSubscribe) v); +// // }); +// // } +// return list; +// } +// } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/config/MqttRedisConfig.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/config/MqttRedisConfig.java deleted file mode 100755 index c0b456e985..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/config/MqttRedisConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.bytedesk.socket.mqtt.config; - -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -import java.io.Serializable; - -/** - * Redis缓存配置类 - * - * @author jackning - */ -@Configuration -@AutoConfigureAfter(RedisAutoConfiguration.class) -public class MqttRedisConfig { - - @Bean - public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { - RedisTemplate template = new RedisTemplate<>(); - template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); - template.setConnectionFactory(redisConnectionFactory); - return template; - } - -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/handler/MqttTransportHandler.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/handler/MqttTransportHandler.java index 04f366a4c6..8c9d1bd924 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/handler/MqttTransportHandler.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/handler/MqttTransportHandler.java @@ -150,9 +150,9 @@ public class MqttTransportHandler extends SimpleChannelInboundHandler clientIdList = mqttClientIdStoreService.get(uid); + // // clientIdList.forEach(clientId -> { + // // // log.info("subscribe clientId {}", clientId); + // // final MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topic, qoS.value()); + // // mqttSubscribeStoreService.put(topic, subscribeStore); + // // }); + // } + + // private void unsubscribe(String uid, String topic) { + // // log.info("unsubscribe uid {}, topic {}", uid, topic); + // // redisUserService.removeTopic(uid, topic); + // // + // // List clientIdList = mqttClientIdStoreService.get(uid); + // // clientIdList.forEach(clientId -> { + // // // log.info("unsubscribe clientId {}", clientId); + // // mqttSubscribeStoreService.remove(topic, clientId); + // // }); + // } + + + +} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPublishMessage.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPublishMessage.java deleted file mode 100755 index a37703798c..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttDupPublishMessage.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.bytedesk.socket.mqtt.model; - -import java.io.Serializable; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -/** - * PUBLISH重发消息存储 - */ -@Data -@Accessors(chain = true) -@NoArgsConstructor -public class MqttDupPublishMessage implements Serializable { - - private static final long serialVersionUID = -8112511377194421600L; - - private String clientId; - - private String topic; - - private int mqttQoS; - - private int messageId; - - private byte[] messageBytes; -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttMqMessage.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttMqMessage.java deleted file mode 100755 index 96e0738c71..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/model/MqttMqMessage.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.bytedesk.socket.mqtt.model; - -import java.io.Serializable; - -import lombok.Data; - -/** - * 发送给MQ消息 - */ -@Data -public class MqttMqMessage implements Serializable { - - private static final long serialVersionUID = -2814339696322592690L; - - private String clientId; - - private String topic; - - private int mqttQoS; - - private byte[] messageBytes; - - private boolean retain; - - private boolean dup; -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Connect.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Connect.java index 9ffaf036c8..21a34df2e1 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Connect.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Connect.java @@ -1,50 +1,23 @@ package com.bytedesk.socket.mqtt.protocol; -// import cn.hutool.core.util.StrUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.*; import io.netty.util.AttributeKey; import io.netty.util.CharsetUtil; -import java.util.Iterator; -import java.util.Set; - import org.springframework.util.StringUtils; -import com.bytedesk.core.redis.RedisUserService; -// import com.bytedesk.core.constant.StatusConsts; -// import com.bytedesk.core.constant.TypeConsts; -// import com.bytedesk.core.model.entity.Group; -// import com.bytedesk.core.model.entity.Thread; -// import com.bytedesk.core.model.entity.User; -// import com.bytedesk.core.model.entity.WorkGroup; -// import com.bytedesk.core.redis.RedisConnectService; -// import com.bytedesk.core.redis.RedisHostService; -// import com.bytedesk.core.redis.RedisRoutingRoundRobin; -// import com.bytedesk.core.redis.RedisStatisticService; -// import com.bytedesk.core.redis.RedisUserService; -// import com.bytedesk.core.service.GroupService; -// import com.bytedesk.core.service.MessageService; -// import com.bytedesk.core.service.ThreadService; -// import com.bytedesk.core.service.UserService; -// import com.bytedesk.core.util.JpaUtil; +import com.bytedesk.core.event.BytedeskEventPublisher; +import com.bytedesk.core.topic.TopicService; import com.bytedesk.socket.mqtt.handler.MqttIdleStateHandler; import com.bytedesk.socket.mqtt.model.MqttSession; -import com.bytedesk.socket.mqtt.model.MqttSubscribe; import com.bytedesk.socket.mqtt.service.*; import com.bytedesk.socket.mqtt.util.MqttConsts; -import com.bytedesk.socket.redis.RedisMessageCacheOfflineService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -// import java.util.Date; -// import java.util.Iterator; -// import java.util.List; -// import java.util.Optional; -// import java.util.Set; - /** * * @author jackning @@ -53,39 +26,21 @@ import lombok.extern.slf4j.Slf4j; @AllArgsConstructor public class Connect { - // private final MqttMqService mqttMqService; - private final MqttAuthService mqttAuthService; - private final MqttSessionStoreService mqttSessionStoreService; + private final MqttSessionService mqttSessionStoreService; - private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; + // private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; - private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + // private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; - private final MqttSubscribeStoreService mqttSubscribeStoreService; + // private final MqttSubscribeService mqttSubscribeStoreService; - private final MqttClientIdStoreService mqttClientIdStoreService; + // private final MqttClientIdService mqttClientIdStoreService; - // private final UserService userService; + private final BytedeskEventPublisher bytedeskEventPublisher; - // private final GroupService groupService; - - // private final ThreadService threadService; - - private final RedisUserService redisUserService; - - // private final RedisRoutingRoundRobin redisRoutingRoundRobin; - - // private final RedisStatisticService redisStatisticService; - - // private final RedisHostService redisHostService; - - // private final RedisConnectService redisConnectService; - - // private final MessageService messageService; - - private final RedisMessageCacheOfflineService redisMessageCacheOfflineService; + private final TopicService topicService; /** * TODO: 重构,每个功能独立成一个函数,精简此函数体 @@ -169,9 +124,10 @@ public class Connect { // 多余需要踢掉线(不同终端后不会互踢,但是两个相同终端(例如两个 iOS 端登录)会互踢。) if (cleanSession.booleanValue()) { mqttSessionStoreService.remove(clientId); - mqttSubscribeStoreService.removeForClient(clientId); - mqttDupPublishMessageStoreService.removeByClient(clientId); - mqttDupPubRelMessageStoreService.removeByClient(clientId); + // mqttSubscribeStoreService.removeForClient(clientId); + topicService.removeClientId(clientId); + // mqttDupPublishMessageStoreService.removeByClient(clientId); + // mqttDupPubRelMessageStoreService.removeByClient(clientId); } // TODO: 修改消息类型 CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD // final MqttConnAckMessage connAckMessage = (MqttConnAckMessage) MqttMessageFactory.newMessage( @@ -204,14 +160,6 @@ public class Connect { Math.round(mqttConnectMessage.variableHeader().keepAliveTimeSeconds() * 1.5f))); } - // 至此存储会话消息及返回接受客户端连接 - mqttSessionStoreService.put(clientId, mqttSession); - - // 将clientId存储到channel的map中 - channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_CLIENT_ID)).set(clientId); - channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_USERNAME)).set(username); - channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_PASSWORD)).set(password); - // 回复 CONNACK 消息给当前客户端 final Boolean sessionPresent = mqttSessionStoreService.containsKey(clientId) && !isCleanSession; final MqttConnAckMessage okResp = (MqttConnAckMessage) MqttMessageFactory.newMessage( @@ -219,224 +167,57 @@ public class Connect { new MqttConnAckVariableHeader(MqttConnectReturnCode.CONNECTION_ACCEPTED, sessionPresent), null); channel.writeAndFlush(okResp); + // 将clientId存储到channel的map中 + channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_CLIENT_ID)).set(clientId); + channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_USERNAME)).set(username); + channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_PASSWORD)).set(password); + + // 至此存储会话消息及返回接受客户端连接 + mqttSessionStoreService.put(clientId, mqttSession); // 存储clientId - mqttClientIdStoreService.put(clientId); + // mqttClientIdStoreService.put(clientId); + topicService.addClientId(clientId); + // + bytedeskEventPublisher.publishMqttConnectedEvent(clientId); // 用户clientId格式: uid/client - final String uid = clientId.split("/")[0]; + // final String uid = clientId.split("/")[0]; // 判断是否已经缓存 - if (redisUserService.hasTopics(uid)) { - // 是否被禁用 - if (redisUserService.isDisabled(uid)) { - return; - } - // 读取缓存 - Set topics = redisUserService.getTopics(uid); - Iterator iterator = topics.iterator(); - while (iterator.hasNext()) { - String topic = iterator.next(); - subscribe(clientId, topic); - } - } else { - // 读取数据库MySQL - subscribeFromMysql(channel, clientId, uid); - } - // 更新在线状态 - updateConnectedStatus(uid); - - // redis查询离线消息,并推送 - sendOfflineMessage(uid); - log.debug("CONNECTED - clientId: {}", clientId); + // subscribe(clientId, uid); + // subscribe(clientId, uid + "/#"); + // // 更新在线状态 + // updateConnectedStatus(uid); + // // redis查询离线消息,并推送 + // sendOfflineMessage(uid); + // log.debug("CONNECTED - clientId: {}", clientId); } - private void subscribeFromMysql(final Channel channel, final String clientId, final String uid) { - - // 用户验证登录成功之后,自动订阅uid主题topic - subscribe(clientId, uid); - - // TODO: 从redis缓存中读取 - // final Optional userOptional = userService.findByUid(uid); - // if (userOptional.isPresent()) { - - // if (userOptional.get().isEnabled()) { - // redisUserService.enable(uid); - // } else { - // redisUserService.disable(uid); - // } - - // // 检查user.enabled是否为true,否则中断连接 - // // 检测账号是否已经到期,如果已经到期,则断开连接 - // // TODO: 从Redis读取 - // if ((!userOptional.get().isEnabled() - // // || (userOptional.get().getValidateUntilDate() != null - // // && userOptional.get().getValidateUntilDate().before(new Date())) - // )) { - // final MqttConnAckMessage connAckMessage = (MqttConnAckMessage) - // MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, - // false, 0), - // new - // MqttConnAckVariableHeader(MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED, - // false), - // null); - // channel.writeAndFlush(connAckMessage); - // channel.close(); - // return; - // } - - // 订阅subDomain主题 - // subscribe(clientId, userOptional.get().getSubDomain()); - - // TODO: 合并以下多次数据库查询为一次查询 - - // 客服:订阅工作组通知消息 - // final Set workGroups = userOptional.get().getWorkGroups(); - // final Iterator iteratorWorkGroups = workGroups.iterator(); - // while (iteratorWorkGroups.hasNext()) { - // final WorkGroup workGroup = iteratorWorkGroups.next(); - // subscribe(clientId, workGroup.getWid()); - // // - // redisUserService.addWorkGroup(uid, workGroup.getWid()); - // } - - // TODO: 每当用户被分配会话,或者加入群组之后,系统发送通知,此mqtt模块监听通知,帮助用户添加订阅 - // final List groupList = groupService.findByDismissedAndMembersContains(false, userOptional.get()); - // final Iterator iterator = groupList.iterator(); - // while (iterator.hasNext()) { - // final Group group = iterator.next(); - // subscribe(clientId, group.getGid()); - // } - - // 客服:重新订阅未结束客服会话 - // List threadList = threadService.findByAgentAndClosed(userOptional.get(), false); - // final Iterator threadIterator = threadList.iterator(); - // while (threadIterator.hasNext()) { - // final Thread thread = threadIterator.next(); - // // 仅针对客服会话 - // if (!thread.getType().equals(TypeConsts.THREAD_TYPE_CONTACT) && !thread.getType().equals(TypeConsts.THREAD_TYPE_GROUP)) { - // subscribe(clientId, thread.getTopic()); - // } - // } - - // 访客:订阅未结束会话 - // List visitorThreadList = threadService.findByVisitorAndClosed(userOptional.get(), false); - // final Iterator visitorThreadIterator = visitorThreadList.iterator(); - // while (visitorThreadIterator.hasNext()) { - // final Thread thread = visitorThreadIterator.next(); - // subscribe(clientId, thread.getTopic()); - // } - - // 添加到客服set集合, 在isAgent函数里面缓存 - // if (userService.isAgent(userOptional.get())) { - // // redisUserService.addAgent(uid); - // } - - // 缓存必要用户信息 - // redisUserService.setAgentInfo(uid, userOptional.get()); - - // TODO: 订阅Channel - // 将在线用户记录入redis,在线计数+1,并利用定时任务记录每分钟在线数 - // eventPublisher.publishUserConnected(userOptional.get()); - // sendDupMessage(isCleanSession); - // } - } - - private void subscribe(String clientId, String topic) { - final String uid = clientId.split("/")[0]; - redisUserService.addTopic(uid, topic); - log.debug("clientid {}, topic {}", clientId, topic); - // - final MqttQoS mqttQoS = MqttQoS.AT_LEAST_ONCE; - final MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topic, mqttQoS.value()); - mqttSubscribeStoreService.put(topic, subscribeStore); - } - - // 更新在线状态 - private void updateConnectedStatus(String uid) { - log.debug("connected {}", uid); - // redisConnectService.cancelDelayDisconnect(uid); - // // - // if (redisUserService.isAgent(uid)) { - // // - // if (!redisUserService.isNoAcceptStatus(uid)) { - // // 只有客服处于接待状态,才会更新下列数据 - // Set workGroupSet = redisUserService.getWorkGroups(uid); - // Iterator iterator = workGroupSet.iterator(); - // while (iterator.hasNext()) { - // String wid = iterator.next(); - // boolean isExist = redisRoutingRoundRobin.isAgentExist(wid, uid); - // if (!isExist) { - // // 将客服添加到队列 - // redisRoutingRoundRobin.addAgent(wid, uid); - // } - // } - // // 缓存客服在线缓存 - // // TODO: 考虑同一个用户,登录多个客户端的情况 - // redisConnectService.addConnectedAgent(uid); - // // 读取缓存 - // User user = redisUserService.getAgentInfo(uid); - // if (user != null) { - // // 统计数据:增加在线客服数量 - // redisStatisticService.addOnlineAgent(user.getSubDomain(), uid); - // // 初始化客服可接待数量,FIXME: 数量有待减掉当前正在进行中会话 - // // redisService.initAgentIdleCount(uid, user.getMaxThreadCount()); - // } - // } - // // 更新管理员所有客服列表信息 - // redisUserService.updateAgentConnected(uid); - // // 设置连接状态为在线 - // userService.updateConnectionStatusByUid(StatusConsts.USER_STATUS_CONNECTED, - // uid); - // } else { - // // 暂时用不到,没必要打开 - // // addConnectedVisitor(user); - // // 通知客服端访客上线 - // messageService.notifyConnectedUid(uid); - // } - // // - // redisConnectService.addConnected(uid); - // // 存储当前用户长连接所在服务器 - // redisHostService.setUserHost(uid, JpaUtil.getServerIp()); - } - - // 发送离线消息 - private void sendOfflineMessage(String uid) { - while (redisMessageCacheOfflineService.length(uid) > 0) { - byte[] messageBytes = redisMessageCacheOfflineService.pop(uid); - if (messageBytes != null) { - // log.debug("send offline message to {}", uid); - // 发送给mq - // messageService.sendProtobufBytesToMqOffline(messageBytes); - } - } - } - - // private void sendDupMessage(boolean isCleanSession) { - // 如果cleanSession为0, 需要重发同一clientId存储的未完成的QoS1和QoS2的DUP消息 - // if (!isCleanSession) { - // log.debug("send dup message"); - // final List dupPublishMessageStoreList = mqttDupPublishMessageStoreService.get(clientId); - // final List dupPubRelMessageStoreList = mqttDupPubRelMessageStoreService.get(clientId); - // dupPublishMessageStoreList.forEach(dupPublishMessage -> { - // final MqttPublishMessage publishMessage = (MqttPublishMessage) - // MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.PUBLISH, true, - // MqttQoS.valueOf(dupPublishMessage.getMqttQoS()), false, 0), - // new MqttPublishVariableHeader(dupPublishMessage.getTopic(), - // dupPublishMessage.getMessageId()), - // Unpooled.buffer().writeBytes(dupPublishMessage.getMessageBytes())); - // channel.writeAndFlush(publishMessage); - // }); - // dupPubRelMessageStoreList.forEach(dupPubRelMessage -> { - // final MqttMessage pubRelMessage = MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.PUBREL,true, - // MqttQoS.AT_MOST_ONCE,false,0), - // MqttMessageIdVariableHeader.from(dupPubRelMessage.getMessageId()), - // null - // ); - // channel.writeAndFlush(pubRelMessage); - // }); - // } + // private void subscribe(String clientId, String topic) { + // // final String uid = clientId.split("/")[0]; + // // redisUserService.addTopic(uid, topic); + // log.debug("clientid {}, topic {}", clientId, topic); + // // + // final MqttQoS mqttQoS = MqttQoS.AT_LEAST_ONCE; + // final MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topic, mqttQoS.value()); + // mqttSubscribeStoreService.put(topic, subscribeStore); // } + // // 更新在线状态 + // private void updateConnectedStatus(String uid) { + // log.debug("connected {}", uid); + // } + + // 发送离线消息 + // private void sendOfflineMessage(String uid) { + // // while (redisMessageCacheOfflineService.length(uid) > 0) { + // // byte[] messageBytes = redisMessageCacheOfflineService.pop(uid); + // // if (messageBytes != null) { + // // // log.debug("send offline message to {}", uid); + // // // 发送给mq + // // // messageService.sendProtobufBytesToMqOffline(messageBytes); + // // } + // // } + // } + + } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/DisConnect.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/DisConnect.java index b1abdcc46d..7a1775be36 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/DisConnect.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/DisConnect.java @@ -5,6 +5,9 @@ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.MqttMessage; + +// import com.bytedesk.core.topic.Topic; +import com.bytedesk.core.topic.TopicService; // import com.bytedesk.core.constant.StatusConsts; // import com.bytedesk.core.model.entity.User; // import com.bytedesk.core.service.MessageService; @@ -18,11 +21,11 @@ import io.netty.handler.codec.mqtt.MqttMessage; // import com.bytedesk.core.redis.RedisStatisticService; // import com.bytedesk.core.redis.RedisUserService; // import com.bytedesk.socket.mqtt.model.MqttSession; -import com.bytedesk.socket.mqtt.service.MqttClientIdStoreService; -import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; -import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; -import com.bytedesk.socket.mqtt.service.MqttSessionStoreService; -import com.bytedesk.socket.mqtt.service.MqttSubscribeStoreService; +// import com.bytedesk.socket.mqtt.service.MqttClientIdService; +// import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; +// import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; +import com.bytedesk.socket.mqtt.service.MqttSessionService; +// import com.bytedesk.socket.mqtt.service.MqttSubscribeService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; // import lombok.extern.slf4j.Slf4j; @@ -34,15 +37,17 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class DisConnect { - private final MqttSessionStoreService mqttSessionStoreService; + private final MqttSessionService mqttSessionStoreService; - private final MqttSubscribeStoreService mqttSubscribeStoreService; + private final TopicService topicService; - private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; + // private final MqttSubscribeService mqttSubscribeStoreService; - private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + // private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; - private final MqttClientIdStoreService mqttClientIdStoreService; + // private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + + // private final MqttClientIdService mqttClientIdStoreService; // private final UserService userService; @@ -72,9 +77,9 @@ public class DisConnect { String clientId = ChannelUtils.getClientId(channel); // final MqttSession sessionStore = mqttSessionStoreService.get(clientId); // if (sessionStore != null && sessionStore.isCleanSession()){ - mqttSubscribeStoreService.removeForClient(clientId); - mqttDupPublishMessageStoreService.removeByClient(clientId); - mqttDupPubRelMessageStoreService.removeByClient(clientId); + // mqttSubscribeStoreService.removeForClient(clientId); + // mqttDupPublishMessageStoreService.removeByClient(clientId); + // mqttDupPubRelMessageStoreService.removeByClient(clientId); // } // 更新离线状态 updateDisconnectedStatus(clientId); @@ -84,7 +89,8 @@ public class DisConnect { // } mqttSessionStoreService.remove(clientId); - mqttClientIdStoreService.remove(clientId); + // mqttClientIdStoreService.remove(clientId); + topicService.removeClientId(clientId); channel.close(); @@ -101,7 +107,7 @@ public class DisConnect { } // 延迟执行,如果客服在此时间段之内重新连接,则不执行 - public void updateDisconnectedStatus(String clientId) { + private void updateDisconnectedStatus(String clientId) { // 用户离线 // final String uid = clientId.split("/")[0]; // log.debug("DisConnect disconnected {}", uid); diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/ProtocolProcess.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/ProtocolProcess.java index 7651292241..a93fecbf5c 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/ProtocolProcess.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/ProtocolProcess.java @@ -1,24 +1,8 @@ package com.bytedesk.socket.mqtt.protocol; import com.bytedesk.core.event.BytedeskEventPublisher; -import com.bytedesk.core.redis.RedisUserService; - -// import com.bytedesk.core.publisher.EventPublisher; -// import com.bytedesk.core.redis.RedisAutoReplyService; -// import com.bytedesk.core.redis.RedisBlockService; -// import com.bytedesk.core.redis.RedisConnectService; -// import com.bytedesk.core.redis.RedisHostService; -// import com.bytedesk.core.redis.RedisRoutingRoundRobin; -// import com.bytedesk.core.redis.RedisSettingService; -// import com.bytedesk.core.redis.RedisStatisticService; -// import com.bytedesk.core.redis.RedisUserService; -// import com.bytedesk.core.service.GroupService; -// import com.bytedesk.core.service.MessageService; -// import com.bytedesk.core.service.ThreadService; -// import com.bytedesk.core.service.UserService; - +import com.bytedesk.core.topic.TopicService; import com.bytedesk.socket.mqtt.service.*; -import com.bytedesk.socket.redis.RedisMessageCacheOfflineService; import lombok.Data; @@ -35,10 +19,10 @@ import org.springframework.stereotype.Component; public class ProtocolProcess { @Autowired - private MqttSessionStoreService mqttSessionStoreService; + private MqttSessionService mqttSessionStoreService; - @Autowired - private MqttSubscribeStoreService mqttSubscribeStoreService; + // @Autowired + // private MqttSubscribeService mqttSubscribeStoreService; @Autowired private MqttAuthService mqttAuthService; @@ -52,54 +36,24 @@ public class ProtocolProcess { @Autowired private MqttMessageIdService mqttMessageIdService; - @Autowired - private MqttRetainMessageStoreService mqttRetainMessageStoreService; + // @Autowired + // private MqttRetainMessageStoreService mqttRetainMessageStoreService; + + // @Autowired + // private MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; + + // @Autowired + // private MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + + // @Autowired + // private MqttClientIdService mqttClientIdStoreService; @Autowired - private MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; - - @Autowired - private MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; - - @Autowired - private MqttClientIdStoreService mqttClientIdStoreService; + private TopicService topicService; @Autowired private BytedeskEventPublisher bytedeskEventPublisher; - // @Autowired - // private UserService userService; - - // @Autowired - // private GroupService groupService; - - // @Autowired - // private ThreadService threadService; - - // @Autowired - // private MessageService messageService; - - @Autowired - private RedisUserService redisUserService; - - // @Autowired - // private RedisRoutingRoundRobin redisRoutingRoundRobin; - - // @Autowired - // private RedisStatisticService redisStatisticService; - - // @Autowired - // private RedisHostService redisHostService; - - // @Autowired - // private RedisConnectService redisConnectService; - - // @Autowired - // private RedisSettingService redisSettingService; - - @Autowired - private RedisMessageCacheOfflineService redisMessageCacheOfflineService; - private Connect connect; private Subscribe subscribe; @@ -125,17 +79,12 @@ public class ProtocolProcess { connect = new Connect( mqttAuthService, mqttSessionStoreService, - mqttDupPublishMessageStoreService, - mqttDupPubRelMessageStoreService, - mqttSubscribeStoreService, - mqttClientIdStoreService, - // userService, groupService, threadService, - // redisUserService, redisRoutingRoundRobin, redisStatisticService, - // redisHostService, - // redisConnectService, - // messageService, - redisUserService, - redisMessageCacheOfflineService); + // mqttDupPublishMessageStoreService, + // mqttDupPubRelMessageStoreService, + // mqttSubscribeStoreService, + // mqttClientIdStoreService, + bytedeskEventPublisher, topicService + ); } return connect; } @@ -143,10 +92,11 @@ public class ProtocolProcess { public Subscribe subscribe() { if (subscribe == null) { subscribe = new Subscribe( - mqttMessageIdService, - mqttSubscribeStoreService, - mqttRetainMessageStoreService - // redisUserService + // mqttMessageIdService, + // mqttSubscribeStoreService + // mqttRetainMessageStoreService + // redisUserService + topicService ); } return subscribe; @@ -154,7 +104,10 @@ public class ProtocolProcess { public UnSubscribe unSubscribe() { if (unSubscribe == null) { - unSubscribe = new UnSubscribe(mqttSubscribeStoreService); + unSubscribe = new UnSubscribe( + // mqttSubscribeStoreService + topicService + ); } return unSubscribe; } @@ -162,7 +115,7 @@ public class ProtocolProcess { public Publish publish() { if (publish == null) { publish = new Publish( - mqttRetainMessageStoreService, + // mqttRetainMessageStoreService, bytedeskEventPublisher // mqttMessageIdService, // mqttClientIdStoreService, @@ -175,16 +128,11 @@ public class ProtocolProcess { public DisConnect disConnect() { if (disConnect == null) { disConnect = new DisConnect( - mqttSessionStoreService, - mqttSubscribeStoreService, - mqttDupPublishMessageStoreService, - mqttDupPubRelMessageStoreService, - mqttClientIdStoreService - // userService, - // redisUserService, redisRoutingRoundRobin, redisStatisticService, - // redisHostService, - // redisConnectService - // messageService + mqttSessionStoreService, topicService + // mqttSubscribeStoreService, + // mqttDupPublishMessageStoreService, + // mqttDupPubRelMessageStoreService, + // mqttClientIdStoreService ); } return disConnect; @@ -206,47 +154,33 @@ public class ProtocolProcess { public PubAck pubAck() { if (pubAck == null) { - pubAck = new PubAck(mqttDupPublishMessageStoreService); + pubAck = new PubAck( + // mqttDupPublishMessageStoreService + ); } return pubAck; } public PubRec pubRec() { if (pubRec == null) { - pubRec = new PubRec(mqttDupPublishMessageStoreService, mqttDupPubRelMessageStoreService); + pubRec = new PubRec( + // mqttDupPublishMessageStoreService, mqttDupPubRelMessageStoreService + ); } return pubRec; } public PubComp pubComp() { if (pubComp == null) { - pubComp = new PubComp(mqttDupPubRelMessageStoreService); + pubComp = new PubComp( + // mqttDupPubRelMessageStoreService + ); } return pubComp; } - public MqttSessionStoreService getMqttSessionStoreService() { + public MqttSessionService getMqttSessionStoreService() { return mqttSessionStoreService; } - // public RedisUserService getRedisUserService() { - // return redisUserService; - // } - - // public RedisService getRedisService() { - // return redisService; - // } - - // public RedisStatisticService getRedisStatisticService() { - // return redisStatisticService; - // } - - // public RedisHostService getRedisHostService() { - // return redisHostService; - // } - - // public RedisConnectService getRedisConnectService() { - // return redisConnectService; - // } - } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubAck.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubAck.java index 151e3122de..084bd9148e 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubAck.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubAck.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:58:48 + * @LastEditTime: 2024-04-15 16:29:23 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,7 +16,7 @@ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; -import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; +// import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,7 +28,7 @@ import lombok.extern.slf4j.Slf4j; @AllArgsConstructor public class PubAck { - private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; + // private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; public void processPubAck(Channel channel, MqttMessageIdVariableHeader variableHeader) { // @@ -36,6 +36,6 @@ public class PubAck { int messageId = variableHeader.messageId(); log.debug("PUBACK - clientId: {}, messageId: {}", clientId, messageId); // - mqttDupPublishMessageStoreService.remove(clientId, messageId); + // mqttDupPublishMessageStoreService.remove(clientId, messageId); } } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubComp.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubComp.java index 14558677dd..41750de389 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubComp.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubComp.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:58:27 + * @LastEditTime: 2024-04-15 16:29:42 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,7 +16,7 @@ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.MqttMessageIdVariableHeader; -import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; +// import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,12 +30,12 @@ import lombok.extern.slf4j.Slf4j; @AllArgsConstructor public class PubComp { - private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + // private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; public void processPubComp(Channel channel, MqttMessageIdVariableHeader variableHeader) { String clientId = ChannelUtils.getClientId(channel); int messageId = variableHeader.messageId(); log.debug("PUBCOMP - clientId: {}, messageId: {}", clientId, messageId); - mqttDupPubRelMessageStoreService.remove(clientId, messageId); + // mqttDupPubRelMessageStoreService.remove(clientId, messageId); } } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubRec.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubRec.java index 2a11ec9e63..754b0f4e6f 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubRec.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/PubRec.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:54:52 + * @LastEditTime: 2024-04-15 16:30:27 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,9 +16,9 @@ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.*; -import com.bytedesk.socket.mqtt.model.MqttDupPubRelMessage; -import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; -import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; +// import com.bytedesk.socket.mqtt.model.MqttDupPubRelMessage; +// import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; +// import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,9 +27,9 @@ import lombok.extern.slf4j.Slf4j; @AllArgsConstructor public class PubRec { - private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; + // private final MqttDupPublishMessageStoreService mqttDupPublishMessageStoreService; - private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; + // private final MqttDupPubRelMessageStoreService mqttDupPubRelMessageStoreService; public void processPubRec(Channel channel, MqttMessageIdVariableHeader variableHeader) { // @@ -37,10 +37,10 @@ public class PubRec { String clientId = ChannelUtils.getClientId(channel); log.debug("PUBREC - clientId: {}, messageId: {}", clientId, messageId); // - mqttDupPublishMessageStoreService.remove(clientId, messageId); + // mqttDupPublishMessageStoreService.remove(clientId, messageId); // - MqttDupPubRelMessage dupPubRelMessageStore = new MqttDupPubRelMessage().setClientId(clientId).setMessageId(messageId); - mqttDupPubRelMessageStoreService.put(clientId, dupPubRelMessageStore); + // MqttDupPubRelMessage dupPubRelMessageStore = new MqttDupPubRelMessage().setClientId(clientId).setMessageId(messageId); + // mqttDupPubRelMessageStoreService.put(clientId, dupPubRelMessageStore); // MqttMessage pubRelMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PUBREL, diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Publish.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Publish.java index 7844173854..c960daab28 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Publish.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Publish.java @@ -1,17 +1,24 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 16:30:45 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.*; import com.bytedesk.core.event.BytedeskEventPublisher; -// import com.bytedesk.core.constant.StatusConsts; -// import com.bytedesk.core.constant.TypeConsts; -// import com.bytedesk.core.redis.RedisBlockService; -// import com.bytedesk.core.redis.RedisSettingService; -// import com.bytedesk.core.service.MessageService; -// import com.bytedesk.core.util.BdDateUtils; -import com.bytedesk.socket.mqtt.model.MqttRetainMessage; -import com.bytedesk.socket.mqtt.service.*; +// import com.bytedesk.socket.mqtt.service.*; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; @@ -21,35 +28,16 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class Publish { - // private final MqttMqService mqttMqService; - - // private final RedisBlockService redisBlockService; - - private final MqttRetainMessageStoreService mqttRetainMessageStoreService; + // private final MqttRetainMessageStoreService mqttRetainMessageStoreService; private final BytedeskEventPublisher bytedeskEventPublisher;; - // private final MessageService messageService; - - // private final MqttMessageIdService mqttMessageIdService; - - // private final MqttClientIdStoreService mqttClientIdStoreService; - - // private final MqttSessionStoreService mqttSessionStoreService; - - // private final RedisSettingService redisSettingService; - - // private final RedisUserService redisUserService; - - // private final RedisThreadService redisThreadService; - // public void processPublish(Channel channel, MqttPublishMessage mqttPublishMessage) { // log.debug("processPublish {}", mqttPublishMessage.toString()); // // TODO: 发送:消息发送成功回执 - // String clientId = (String) - // channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_CLIENT_ID)).get(); + // String clientId = (String) channel.attr(AttributeKey.valueOf(MqttConsts.MQTT_CLIENT_ID)).get(); byte[] messageBytes = new byte[mqttPublishMessage.payload().readableBytes()]; this.sendMqMessage(mqttPublishMessage, messageBytes); // QoS=0 @@ -58,12 +46,10 @@ public class Publish { } // QoS=1 else if (mqttPublishMessage.fixedHeader().qosLevel() == MqttQoS.AT_LEAST_ONCE) { - // this.sendMqMessage(clientId, mqttPublishMessage, messageBytes); ChannelUtils.sendPubAckMessage(channel, mqttPublishMessage.variableHeader().packetId()); } // QoS=2 else if (mqttPublishMessage.fixedHeader().qosLevel() == MqttQoS.EXACTLY_ONCE) { - // this.sendMqMessage(clientId, mqttPublishMessage, messageBytes); ChannelUtils.sendPubRecMessage(channel, mqttPublishMessage.variableHeader().packetId()); } // retain=1, 保留消息 @@ -71,85 +57,24 @@ public class Publish { // mqttPublishMessage.payload().getBytes(mqttPublishMessage.payload().readerIndex(), messageBytes); // - if (messageBytes.length == 0) { - mqttRetainMessageStoreService.remove(mqttPublishMessage.variableHeader().topicName()); - } else { - MqttRetainMessage retainMessageStore = new MqttRetainMessage() - .setTopic(mqttPublishMessage.variableHeader().topicName()) - .setMqttQoS(mqttPublishMessage.fixedHeader().qosLevel().value()).setMessageBytes(messageBytes); - mqttRetainMessageStoreService.put(mqttPublishMessage.variableHeader().topicName(), retainMessageStore); - } + // if (messageBytes.length == 0) { + // mqttRetainMessageStoreService.remove(mqttPublishMessage.variableHeader().topicName()); + // } else { + // MqttRetainMessage retainMessageStore = new MqttRetainMessage() + // .setTopic(mqttPublishMessage.variableHeader().topicName()) + // .setMqttQoS(mqttPublishMessage.fixedHeader().qosLevel().value()).setMessageBytes(messageBytes); + // mqttRetainMessageStoreService.put(mqttPublishMessage.variableHeader().topicName(), retainMessageStore); + // } } + } // 下列过滤不能从直接数据库中读取,否则会增加数据库压力,影响消息发送速度,务必从内存或redis中读取 private void sendMqMessage(MqttPublishMessage publishMessage, byte[] messageBytes) { // 注意:不能去掉,否则无法解析protobuf publishMessage.payload().getBytes(publishMessage.payload().readerIndex(), messageBytes); - // TODO: 发送-发送成功回执 // publish messsage event, developers can listener to new message bytedeskEventPublisher.publishMessageBytesEvent(messageBytes); } - // private void sendPubAckMessage(Channel channel, int messageId) { - // // - // MqttPubAckMessage pubAckMessage = (MqttPubAckMessage) MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0), - // MqttMessageIdVariableHeader.from(messageId), - // null); - // channel.writeAndFlush(pubAckMessage); - // } - - // private void sendPubRecMessage(Channel channel, int messageId) { - // // - // MqttMessage pubRecMessage = MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.AT_MOST_ONCE, false, 0), - // MqttMessageIdVariableHeader.from(messageId), null); - // channel.writeAndFlush(pubRecMessage); - // } - - // private void doSendReceiptToSenderClients(String senderUid, MessageProto.Message messageProto) { - // String topic = messageProto.getThread().getTopic(); - // byte[] messageByteArray = messageProto.toByteArray(); - // // - // List clientIdList = mqttClientIdStoreService.get(senderUid); - // // FIXME: java.util.ConcurrentModificationException: null at - // // java.util.ArrayList.forEach(ArrayList.java:1260) ~[na:1.8.0_192] - // clientIdList.forEach(clientId -> { - // if (StringUtils.hasLength(clientId) && (mqttSessionStoreService != null)) { - // // log.info("doSendReceiptToSenderClients clientId {}", clientId); - // int messageId = mqttMessageIdService.getNextMessageId(); - // MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( - // new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0), - // new MqttPublishVariableHeader(topic, messageId), - // Unpooled.buffer().writeBytes(messageByteArray)); - // // 只会发送给当前在同一台服务器上的登录客户端 - // MqttSession mqttSession = mqttSessionStoreService.get(clientId); - // if (mqttSession != null) { - // Channel channel = mqttSession.getChannel(); - // if (channel != null) { - // channel.writeAndFlush(publishMessage); - // } else { - // log.error("FIXME: channel is null"); - // // 发送给mq - // // messageService.sendProtobufBytesToMq(messageByteArray); - // } - // } else { - // log.error("FIXME: mqttSession is null"); - // // 发送给mq - // // messageService.sendProtobufBytesToMq(messageByteArray); - // } - // // mqttSessionStoreService.get(clientId).getChannel().writeAndFlush(publishMessage); - // } else if (!StringUtils.hasLength(clientId)) { - // log.error("FIXME: doSendReceiptToSenderClients clientId {}", clientId); - // // 发送给mq - // // messageService.sendProtobufBytesToMq(messageByteArray); - // } else { - // log.error("FIXME: mqttSessionStoreService is null"); - // // 发送给mq - // // messageService.sendProtobufBytesToMq(messageByteArray); - // } - // }); - // } - } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Subscribe.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Subscribe.java index e24ecaa7d5..c95ee46c7f 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Subscribe.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/Subscribe.java @@ -1,14 +1,13 @@ package com.bytedesk.socket.mqtt.protocol; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.*; + +import com.bytedesk.core.topic.TopicService; // import com.bytedesk.core.redis.RedisUserService; -import com.bytedesk.socket.mqtt.model.MqttRetainMessage; -import com.bytedesk.socket.mqtt.model.MqttSubscribe; -import com.bytedesk.socket.mqtt.service.MqttMessageIdService; -import com.bytedesk.socket.mqtt.service.MqttRetainMessageStoreService; -import com.bytedesk.socket.mqtt.service.MqttSubscribeStoreService; +// import com.bytedesk.socket.mqtt.model.MqttRetainMessage; +// import com.bytedesk.socket.mqtt.model.MqttSubscribe; +// import com.bytedesk.socket.mqtt.service.MqttSubscribeService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import com.bytedesk.socket.mqtt.util.MqttUtil; import lombok.AllArgsConstructor; @@ -24,14 +23,16 @@ import java.util.List; @AllArgsConstructor public class Subscribe { - private final MqttMessageIdService mqttMessageIdService; + // private final MqttMessageIdService mqttMessageIdService; - private final MqttSubscribeStoreService mqttSubscribeStoreService; + // private final MqttSubscribeService mqttSubscribeStoreService; - private final MqttRetainMessageStoreService mqttRetainMessageStoreService; + // private final MqttRetainMessageStoreService mqttRetainMessageStoreService; // private final RedisUserService redisUserService; + private final TopicService topicService; + public void processSubscribe(Channel channel, MqttSubscribeMessage mqttSubscribeMessage) { // log.debug("processSubscribe {}", mqttSubscribeMessage.toString()); // @@ -52,9 +53,11 @@ public class Subscribe { String topicFilter = topicSubscription.topicName(); MqttQoS mqttQoS = topicSubscription.qualityOfService(); - MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topicFilter, mqttQoS.value()); + // MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topicFilter, mqttQoS.value()); // - mqttSubscribeStoreService.put(topicFilter, subscribeStore); + // mqttSubscribeStoreService.put(topicFilter, subscribeStore); + topicService.subscribe(topicFilter, clientId); + // mqttQoSList.add(mqttQoS.value()); // 添加缓存 // redisUserService.addTopic(uid, topicFilter); @@ -81,44 +84,45 @@ public class Subscribe { } // 发布当前topic的retain消息给当前客户端 - public void sendRetainMessage(Channel channel, String topicFilter, MqttQoS mqttQoS) { - // - List retainMessageStores = mqttRetainMessageStoreService.search(topicFilter); - retainMessageStores.forEach(retainMessage -> { - // - MqttQoS respQoS = retainMessage.getMqttQoS() > mqttQoS.value() ? mqttQoS - : MqttQoS.valueOf(retainMessage.getMqttQoS()); - // - String clientId = ChannelUtils.getClientId(channel); + // private void sendRetainMessage(Channel channel, String topicFilter, MqttQoS mqttQoS) { + // // + // // List retainMessageStores = mqttRetainMessageStoreService.search(topicFilter); + // // retainMessageStores.forEach(retainMessage -> { + // // // + // // MqttQoS respQoS = retainMessage.getMqttQoS() > mqttQoS.value() ? mqttQoS + // // : MqttQoS.valueOf(retainMessage.getMqttQoS()); + // // // + // // String clientId = ChannelUtils.getClientId(channel); - if (respQoS == MqttQoS.AT_MOST_ONCE) { - // - MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( - new MqttFixedHeader(MqttMessageType.PUBLISH, false, respQoS, false, 0), - new MqttPublishVariableHeader(retainMessage.getTopic(), 0), - Unpooled.buffer().writeBytes(retainMessage.getMessageBytes())); - // + // // if (respQoS == MqttQoS.AT_MOST_ONCE) { + // // // + // // MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( + // // new MqttFixedHeader(MqttMessageType.PUBLISH, false, respQoS, false, 0), + // // new MqttPublishVariableHeader(retainMessage.getTopic(), 0), + // // Unpooled.buffer().writeBytes(retainMessage.getMessageBytes())); + // // // - log.debug("PUBLISH - clientId: {}, topic: {}, Qos: {}", - clientId, retainMessage.getTopic(), - respQoS.value()); + // // log.debug("PUBLISH - clientId: {}, topic: {}, Qos: {}", + // // clientId, retainMessage.getTopic(), + // // respQoS.value()); - channel.writeAndFlush(publishMessage); + // // channel.writeAndFlush(publishMessage); - } else if (respQoS == MqttQoS.AT_LEAST_ONCE || respQoS == MqttQoS.EXACTLY_ONCE) { - // - int messageId = mqttMessageIdService.getNextMessageId(); - MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( - new MqttFixedHeader(MqttMessageType.PUBLISH, false, respQoS, false, 0), - new MqttPublishVariableHeader(retainMessage.getTopic(), messageId), - Unpooled.buffer().writeBytes(retainMessage.getMessageBytes())); - // - log.debug("PUBLISH - clientId: {}, topic: {}, Qos: {}, messageId: {}", clientId, retainMessage.getTopic(), - respQoS.value(), messageId); + // // } else if (respQoS == MqttQoS.AT_LEAST_ONCE || respQoS == MqttQoS.EXACTLY_ONCE) { + // // // + // // int messageId = mqttMessageIdService.getNextMessageId(); + // // MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( + // // new MqttFixedHeader(MqttMessageType.PUBLISH, false, respQoS, false, 0), + // // new MqttPublishVariableHeader(retainMessage.getTopic(), messageId), + // // Unpooled.buffer().writeBytes(retainMessage.getMessageBytes())); + // // // + // // log.debug("PUBLISH - clientId: {}, topic: {}, Qos: {}, messageId: {}", clientId, retainMessage.getTopic(), + // // respQoS.value(), messageId); - channel.writeAndFlush(publishMessage); - } - }); - } + // // channel.writeAndFlush(publishMessage); + // // } + // // }); + + // } } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/UnSubscribe.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/UnSubscribe.java index 1503b4935d..ef2b697b2b 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/UnSubscribe.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/protocol/UnSubscribe.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:52:49 + * @LastEditTime: 2024-04-16 10:11:18 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,8 +16,10 @@ package com.bytedesk.socket.mqtt.protocol; import io.netty.channel.Channel; import io.netty.handler.codec.mqtt.*; + +import com.bytedesk.core.topic.TopicService; // import com.bytedesk.core.redis.RedisUserService; -import com.bytedesk.socket.mqtt.service.MqttSubscribeStoreService; +// import com.bytedesk.socket.mqtt.service.MqttSubscribeService; import com.bytedesk.socket.mqtt.util.ChannelUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,10 +30,12 @@ import java.util.List; @AllArgsConstructor public class UnSubscribe { - private final MqttSubscribeStoreService mqttSubscribeStoreService; + // private final MqttSubscribeService mqttSubscribeStoreService; // private final RedisUserService redisUserService; + private final TopicService topicService; + public void processUnSubscribe(Channel channel, MqttUnsubscribeMessage mqttUnsubscribeMessage) { // log.debug("processUnSubscribe {}", mqttUnsubscribeMessage.toString()); // @@ -42,7 +46,8 @@ public class UnSubscribe { // final String uid = clientId.split("/")[0]; topicFilters.forEach(topicFilter -> { // - mqttSubscribeStoreService.remove(topicFilter, clientId); + // mqttSubscribeStoreService.remove(topicFilter, clientId); + topicService.unsubscribe(topicFilter, clientId); // 移除缓存 // redisUserService.removeTopic(uid, topicFilter); log.debug("UNSUBSCRIBE - clientId: {}, topicFilter: {}", clientId, topicFilter); diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPubRelMessageCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPubRelMessageCache.java deleted file mode 100755 index f78413eeca..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPubRelMessageCache.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttDupPubRelMessage; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * - * @author jackning - */ -@Service -public class MqttDupPubRelMessageCache { - - public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "pubrel:"; - - @Autowired - private RedisTemplate redisTemplate; - - public MqttDupPubRelMessage put(String clientId, Integer messageId, MqttDupPubRelMessage dupPubRelMessageStore) { - - redisTemplate.opsForHash().put(CACHE_PRE + clientId, String.valueOf(messageId), dupPubRelMessageStore); - - return dupPubRelMessageStore; - } - - public ConcurrentHashMap get(String clientId) { - - ConcurrentHashMap map = new ConcurrentHashMap<>(); - - Map map1 = redisTemplate.opsForHash().entries(CACHE_PRE + clientId); - if (map1 != null && !map1.isEmpty()) { - map1.forEach((k, v) -> { - map.put((Integer) k, (MqttDupPubRelMessage) v); - }); - } - - return map; - } - - public boolean containsKey(String clientId) { - - return redisTemplate.hasKey(CACHE_PRE + clientId); - } - - public void remove(String clientId, Integer messageId) { - - redisTemplate.opsForHash().delete(CACHE_PRE + clientId, messageId); - } - - public void remove(String clientId) { - - redisTemplate.delete(CACHE_PRE + clientId); - } - -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPublishMessageCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPublishMessageCache.java deleted file mode 100755 index 6b0fb93467..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttDupPublishMessageCache.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:22:04 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttDupPublishMessage; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author jackning - */ -@Service -public class MqttDupPublishMessageCache { - - private final static String CACHE_PRE = MqttConsts.MQTT_PREFIX + "publish:"; - - @Autowired - private RedisTemplate redisTemplate; - - public MqttDupPublishMessage put(String clientId, Integer messageId, MqttDupPublishMessage dupPublishMessageStore) { - - redisTemplate.opsForHash().put(CACHE_PRE + clientId, String.valueOf(messageId), dupPublishMessageStore); - - return dupPublishMessageStore; - } - - public ConcurrentHashMap get(String clientId) { - - ConcurrentHashMap map = new ConcurrentHashMap<>(); - - Map map1 = redisTemplate.opsForHash().entries(CACHE_PRE + clientId); - - if (map1 != null && !map1.isEmpty()) { - map1.forEach((k, v) -> { - // FIXME: java.lang.ClassCastException: java.lang.String cannot be cast to - // java.lang.Integer - map.put((String) k, (MqttDupPublishMessage) v); - }); - } - - return map; - } - - public boolean containsKey(String clientId) { - return redisTemplate.hasKey(CACHE_PRE + clientId); - } - - public void remove(String clientId, Integer messageId) { - redisTemplate.opsForHash().delete(CACHE_PRE + clientId, String.valueOf(messageId)); - } - - public void remove(String clientId) { - redisTemplate.delete(CACHE_PRE + clientId); - } - -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttRetainMessageCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttRetainMessageCache.java deleted file mode 100755 index 211eba9678..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttRetainMessageCache.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:41:15 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttRetainMessage; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * @author jackning - */ -@Service -public class MqttRetainMessageCache { - - public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "retain:"; - - @Autowired - private RedisTemplate redisTemplate; - - public MqttRetainMessage put(String topic, MqttRetainMessage retainMessageStore) { - - redisTemplate.opsForValue().set(CACHE_PRE + topic, retainMessageStore); - - return retainMessageStore; - } - - public MqttRetainMessage get(String topic) { - return (MqttRetainMessage) redisTemplate.opsForValue().get(CACHE_PRE + topic); - } - - public boolean containsKey(String topic) { - return redisTemplate.hasKey(CACHE_PRE + topic); - } - - public void remove(String topic) { - redisTemplate.delete(CACHE_PRE + topic); - } - - public Map all() { - Map map = new HashMap<>(); - Set set = redisTemplate.keys(CACHE_PRE + "*"); - if (set != null && !set.isEmpty()) { - set.forEach(entry -> map.put(entry.substring(CACHE_PRE.length()), - (MqttRetainMessage) redisTemplate.opsForValue().get(entry))); - } - return map; - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSessionCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSessionCache.java deleted file mode 100755 index 3afc9649dc..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSessionCache.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttSession; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * - * - * @author jackning - */ -@Service -public class MqttSessionCache { - - public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "session:"; - - @Autowired - private RedisTemplate redisTemplate; - - public MqttSession put(String clientId, MqttSession mqttSession) { - - redisTemplate.opsForValue().set(CACHE_PRE + clientId, mqttSession); - - return mqttSession; - } - - public MqttSession get(String clientId) { - - return (MqttSession) redisTemplate.opsForValue().get(CACHE_PRE + clientId); - } - - public boolean containsKey(String clientId) { - - return redisTemplate.hasKey(CACHE_PRE + clientId); - } - - public void remove(String clientId) { - - redisTemplate.delete(CACHE_PRE + clientId); - } - - public Map all() { - Map map = new HashMap<>(); - Set set = redisTemplate.keys(CACHE_PRE + "*"); - if (set != null && !set.isEmpty()) { - set.forEach(entry -> map.put(entry.substring(CACHE_PRE.length()), - (MqttSession) redisTemplate.opsForValue().get(entry))); - } - return map; - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeNotWildcardCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeNotWildcardCache.java deleted file mode 100755 index 030a631cbd..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeNotWildcardCache.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttSubscribe; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author jackning - */ -@Service -public class MqttSubscribeNotWildcardCache { - - public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "subnotwildcard:"; - - public static final String CACHE_CLIENT_PRE = MqttConsts.MQTT_PREFIX + "client:"; - - @Autowired - private StringRedisTemplate stringRedisTemplate; - - @Autowired - private RedisTemplate redisTemplate; - - // org.springframework.data.redis.serializer.SerializationException: - // Cannot serialize; nested exception is - // org.springframework.core.serializer.support.SerializationFailedException: - // Failed to serialize object using DefaultSerializer; - // FIXME: nested exception is java.lang.OutOfMemoryError: GC overhead limit - // exceeded - public MqttSubscribe put(String topic, String clientId, MqttSubscribe mqttSubscribe) { - redisTemplate.opsForHash().put(CACHE_PRE + topic, clientId, mqttSubscribe); - stringRedisTemplate.opsForSet().add(CACHE_CLIENT_PRE + clientId, topic); - return mqttSubscribe; - } - - public MqttSubscribe get(String topic, String clientId) { - return (MqttSubscribe) redisTemplate.opsForHash().get(CACHE_PRE + topic, clientId); - } - - public boolean containsKey(String topic, String clientId) { - return redisTemplate.opsForHash().hasKey(CACHE_PRE + topic, clientId); - } - - public void remove(String topic, String clientId) { - stringRedisTemplate.opsForSet().remove(CACHE_CLIENT_PRE + clientId, topic); - redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); - } - - public void removeForClient(String clientId) { - for (String topic : stringRedisTemplate.opsForSet().members(CACHE_CLIENT_PRE + clientId)) { - redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); - } - stringRedisTemplate.delete(CACHE_CLIENT_PRE + clientId); - } - - public Map> all() { - Map> map = new HashMap<>(); - Set set = redisTemplate.keys(CACHE_PRE + "*"); - if (set != null && !set.isEmpty()) { - set.forEach( - entry -> { - ConcurrentHashMap map1 = new ConcurrentHashMap<>(); - Map map2 = redisTemplate.opsForHash().entries(entry); - if (map2 != null && !map2.isEmpty()) { - map2.forEach((k, v) -> { - map1.put((String) k, (MqttSubscribe) v); - }); - map.put(entry.substring(CACHE_PRE.length()), map1); - } - }); - } - return map; - } - - public List all(String topic) { - List list = new ArrayList<>(); - Map map = redisTemplate.opsForHash().entries(CACHE_PRE + topic); - if (map != null && !map.isEmpty()) { - map.forEach((k, v) -> { - list.add((MqttSubscribe) v); - }); - } - return list; - } - -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeWildcardCache.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeWildcardCache.java deleted file mode 100755 index 6d8bbf114c..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/redis/MqttSubscribeWildcardCache.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.redis; - -import com.bytedesk.socket.mqtt.model.MqttSubscribe; -import com.bytedesk.socket.mqtt.util.MqttConsts; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * - * @author jackning - */ -@Service -public class MqttSubscribeWildcardCache { - - public static final String CACHE_PRE = MqttConsts.MQTT_PREFIX + "subwildcard:"; - - public static final String CACHE_CLIENT_PRE = MqttConsts.MQTT_PREFIX + "client:"; - - @Autowired - private StringRedisTemplate stringRedisTemplate; - - @Autowired - private RedisTemplate redisTemplate; - - public MqttSubscribe put(String topic, String clientId, MqttSubscribe subscribeStore) { - redisTemplate.opsForHash().put(CACHE_PRE + topic, clientId, subscribeStore); - stringRedisTemplate.opsForSet().add(CACHE_CLIENT_PRE + clientId, topic); - return subscribeStore; - } - - public MqttSubscribe get(String topic, String clientId) { - return (MqttSubscribe) redisTemplate.opsForHash().get(CACHE_PRE + topic, clientId); - } - - public boolean containsKey(String topic, String clientId) { - return redisTemplate.opsForHash().hasKey(CACHE_PRE + topic, clientId); - } - - public void remove(String topic, String clientId) { - stringRedisTemplate.opsForSet().remove(CACHE_CLIENT_PRE + clientId, topic); - redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); - } - - public void removeForClient(String clientId) { - for (String topic : stringRedisTemplate.opsForSet().members(CACHE_CLIENT_PRE + clientId)) { - redisTemplate.opsForHash().delete(CACHE_PRE + topic, clientId); - } - stringRedisTemplate.delete(CACHE_CLIENT_PRE + clientId); - } - - public Map> all() { - Map> map = new HashMap<>(); - Set set = redisTemplate.keys(CACHE_PRE + "*"); - if (set != null && !set.isEmpty()) { - set.forEach( - entry -> { - ConcurrentHashMap map1 = new ConcurrentHashMap<>(); - Map map2 = redisTemplate.opsForHash().entries(entry); - if (map2 != null && !map2.isEmpty()) { - map2.forEach((k, v) -> { - map1.put((String) k, (MqttSubscribe) v); - }); - map.put(entry.substring(CACHE_PRE.length()), map1); - } - }); - } - return map; - } - - public List all(String topic) { - List list = new ArrayList<>(); - Map map = redisTemplate.opsForHash().entries(CACHE_PRE + topic); - if (map != null && !map.isEmpty()) { - map.forEach((k, v) -> { - list.add((MqttSubscribe) v); - }); - } - return list; - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/server/MqttServer.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/server/MqttServer.java index 8729875325..d44d084321 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/server/MqttServer.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/server/MqttServer.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-07 22:59:20 + * @LastEditTime: 2024-04-13 11:45:26 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -32,6 +32,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** + * current version based on 3.1.1 + * 当前版本基于 mqtt3.1.1 + * @see https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html + * @see https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html + * * encrypt/security through nginx proxy * * @author jackning diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttAuthService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttAuthService.java index 06388d7f8b..d6bb04dd4d 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttAuthService.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttAuthService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 11:06:17 + * @LastEditTime: 2024-04-15 14:49:18 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,7 +16,7 @@ package com.bytedesk.socket.mqtt.service; import com.bytedesk.socket.mqtt.service.MqttAuthService; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -26,7 +26,7 @@ import org.springframework.stereotype.Service; * * @author jackning */ -@Slf4j +// @Slf4j @Service @AllArgsConstructor public class MqttAuthService { @@ -37,7 +37,7 @@ public class MqttAuthService { // TODO: 待实现 public boolean checkValid(String username, String password) { - log.debug("auth username {}, password {}", username, password); + // log.debug("auth username {}, password {}", username, password); // 客户端使用accessToken作为password传递,避免客户端存储密码 diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdService.java new file mode 100644 index 0000000000..32e90fd2e8 --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdService.java @@ -0,0 +1,77 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 09:33:13 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.mqtt.service; + +// import java.util.ArrayList; +// import java.util.Collections; +// import java.util.List; +// import java.util.Map; +// import java.util.concurrent.ConcurrentHashMap; + +// import org.springframework.stereotype.Service; + +// import com.bytedesk.socket.mqtt.service.MqttClientIdService; +// import lombok.AllArgsConstructor; +// // import lombok.extern.slf4j.Slf4j; + +// /** +// * +// */ +// // @Slf4j +// @Service +// @AllArgsConstructor +// public class MqttClientIdService { + +// private Map> clientIdCache = new ConcurrentHashMap<>(); + +// public void put(String clientId) { +// final String uid = clientId.split("/")[0]; +// List clientIdList = clientIdCache.get(uid); +// if (clientIdList == null) { +// // 加锁 +// clientIdList = Collections.synchronizedList(new ArrayList<>()); +// } +// if (!clientIdList.contains(clientId)) { +// clientIdList.add(clientId); +// clientIdCache.put(uid, clientIdList); +// } +// } + +// public List get(String uid) { +// List clientIdList = clientIdCache.get(uid); +// if (clientIdList == null) { +// return new ArrayList<>(); +// } +// return clientIdList; +// } + +// public void remove(String clientId) { +// final String uid = clientId.split("/")[0]; +// List clientIdList = clientIdCache.get(uid); +// if (clientIdList != null && clientIdList.contains(clientId)) { +// clientIdList.remove(clientId); +// } +// } + +// public boolean contains(String clientId) { +// final String uid = clientId.split("/")[0]; +// List clientIdList = clientIdCache.get(uid); +// if (clientIdList != null && clientIdList.contains(clientId)) { +// return true; +// } +// return false; +// } + +// } \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdStoreService.java deleted file mode 100644 index d3095b70e9..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttClientIdStoreService.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:04:46 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.service; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.stereotype.Service; - -import com.bytedesk.socket.mqtt.service.MqttClientIdStoreService; -import lombok.AllArgsConstructor; -// import lombok.extern.slf4j.Slf4j; - -/** - * - */ -// @Slf4j -@Service -@AllArgsConstructor -public class MqttClientIdStoreService { - - private Map> clientIdCache = new ConcurrentHashMap<>(); - - public void put(String clientId) { - final String uid = clientId.split("/")[0]; - List clientIdList = clientIdCache.get(uid); - if (clientIdList == null) { - // 加锁 - clientIdList = Collections.synchronizedList(new ArrayList<>()); - } - if (!clientIdList.contains(clientId)) { - clientIdList.add(clientId); - clientIdCache.put(uid, clientIdList); - } - } - - public List get(String uid) { - List clientIdList = clientIdCache.get(uid); - if (clientIdList == null) { - return new ArrayList<>(); - } - return clientIdList; - } - - public void remove(String clientId) { - final String uid = clientId.split("/")[0]; - List clientIdList = clientIdCache.get(uid); - if (clientIdList != null && clientIdList.contains(clientId)) { - clientIdList.remove(clientId); - } - } - - public boolean contains(String clientId) { - final String uid = clientId.split("/")[0]; - List clientIdList = clientIdCache.get(uid); - if (clientIdList != null && clientIdList.contains(clientId)) { - return true; - } - return false; - } - -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPubRelMessageStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPubRelMessageStoreService.java deleted file mode 100755 index 8da6148de4..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPubRelMessageStoreService.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:05:42 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.service; - -import com.bytedesk.socket.mqtt.model.MqttDupPubRelMessage; -import com.bytedesk.socket.mqtt.service.MqttDupPubRelMessageStoreService; -import com.bytedesk.socket.mqtt.redis.MqttDupPubRelMessageCache; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -@Service -@AllArgsConstructor -public class MqttDupPubRelMessageStoreService { - - private final MqttDupPubRelMessageCache mqttDupPubRelMessageCache; - - public void put(String clientId, MqttDupPubRelMessage dupPubRelMessageStore) { - mqttDupPubRelMessageCache.put(clientId, dupPubRelMessageStore.getMessageId(), dupPubRelMessageStore); - } - - public List get(String clientId) { - if (mqttDupPubRelMessageCache.containsKey(clientId)) { - ConcurrentHashMap map = mqttDupPubRelMessageCache.get(clientId); - Collection collection = map.values(); - return new ArrayList<>(collection); - } - return new ArrayList<>(); - } - - public void remove(String clientId, int messageId) { - mqttDupPubRelMessageCache.remove(clientId, messageId); - } - - public void removeByClient(String clientId) { - if (mqttDupPubRelMessageCache.containsKey(clientId)) { - mqttDupPubRelMessageCache.remove(clientId); - } - } - -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPublishMessageStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPublishMessageStoreService.java deleted file mode 100755 index a69c837597..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttDupPublishMessageStoreService.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:05:10 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.service; - -import com.bytedesk.socket.mqtt.model.MqttDupPublishMessage; -import com.bytedesk.socket.mqtt.service.MqttDupPublishMessageStoreService; -import com.bytedesk.socket.mqtt.redis.MqttDupPublishMessageCache; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -@Service -@AllArgsConstructor -public class MqttDupPublishMessageStoreService { - - private final MqttDupPublishMessageCache mqttDupPublishMessageCache; - - public void put(String clientId, MqttDupPublishMessage dupPublishMessageStore) { - mqttDupPublishMessageCache.put(clientId, dupPublishMessageStore.getMessageId(), dupPublishMessageStore); - } - - public List get(String clientId) { - if (mqttDupPublishMessageCache.containsKey(clientId)) { - ConcurrentHashMap map = mqttDupPublishMessageCache.get(clientId); - Collection collection = map.values(); - return new ArrayList<>(collection); - } - return new ArrayList<>(); - } - - public void remove(String clientId, int messageId) { - mqttDupPublishMessageCache.remove(clientId, messageId); - } - - public void removeByClient(String clientId) { - mqttDupPublishMessageCache.remove(clientId); - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttRetainMessageStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttRetainMessageStoreService.java deleted file mode 100755 index 08f3b7bdd6..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttRetainMessageStoreService.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 11:53:03 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.service; - -import cn.hutool.core.util.StrUtil; -import com.bytedesk.socket.mqtt.service.MqttRetainMessageStoreService; -import com.bytedesk.socket.mqtt.model.MqttRetainMessage; -import com.bytedesk.socket.mqtt.redis.MqttRetainMessageCache; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; - -@Service -@AllArgsConstructor -public class MqttRetainMessageStoreService { - - private final MqttRetainMessageCache mqttRetainMessageCache; - - public void put(String topic, MqttRetainMessage retainMessageStore) { - mqttRetainMessageCache.put(topic, retainMessageStore); - } - - public MqttRetainMessage get(String topic) { - return mqttRetainMessageCache.get(topic); - } - - public void remove(String topic) { - mqttRetainMessageCache.remove(topic); - } - - public boolean containsKey(String topic) { - return mqttRetainMessageCache.containsKey(topic); - } - - public List search(String topicFilter) { - List retainMessageStores = new ArrayList(); - if (!StrUtil.contains(topicFilter, '#') && !StrUtil.contains(topicFilter, '+')) { - if (mqttRetainMessageCache.containsKey(topicFilter)) { - retainMessageStores.add(mqttRetainMessageCache.get(topicFilter)); - } - } else { - mqttRetainMessageCache.all().forEach((topic, val) -> { - if (StrUtil.split(topic, '/').size() >= StrUtil.split(topicFilter, '/').size()) { - List splitTopics = StrUtil.split(topic, '/'); - List spliteTopicFilters = StrUtil.split(topicFilter, '/'); - String newTopicFilter = ""; - for (int i = 0; i < spliteTopicFilters.size(); i++) { - String value = spliteTopicFilters.get(i); - if (value.equals("+")) { - newTopicFilter = newTopicFilter + "+/"; - } else if (value.equals("#")) { - newTopicFilter = newTopicFilter + "#/"; - break; - } else { - newTopicFilter = newTopicFilter + splitTopics.get(i) + "/"; - } - } - newTopicFilter = StrUtil.removeSuffix(newTopicFilter, "/"); - if (topicFilter.equals(newTopicFilter)) { - retainMessageStores.add(val); - } - } - }); - } - return retainMessageStores; - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionService.java similarity index 82% rename from modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionStoreService.java rename to modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionService.java index 66d8d1a933..ed8cde5ead 100755 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionStoreService.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSessionService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-22 15:07:13 + * @LastEditTime: 2024-04-16 09:23:00 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -26,36 +26,29 @@ import java.util.concurrent.ConcurrentHashMap; /** * 会话存储接口类 * - * TODO: redis持久化 */ // @Slf4j @Service @AllArgsConstructor -public class MqttSessionStoreService { +public class MqttSessionService { private Map clientidSessionMap = new ConcurrentHashMap<>(); - // private final MqttSessionCache mqttSessionCache; - public void put(String clientId, MqttSession mqttSession) { clientidSessionMap.put(clientId, mqttSession); - // mqttSessionCache.put(clientId, mqttSession); } public MqttSession get(String clientId) { return clientidSessionMap.get(clientId); - // return mqttSessionCache.get(clientId); } public boolean containsKey(String clientId) { // log.debug("clientidSessionMap are: {}", clientidSessionMap); return clientidSessionMap.containsKey(clientId); - // return mqttSessionCache.containsKey(clientId); } public void remove(String clientId) { clientidSessionMap.remove(clientId); - // mqttSessionCache.remove(clientId); } } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeService.java new file mode 100755 index 0000000000..0a6e54d282 --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeService.java @@ -0,0 +1,67 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 09:33:24 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.mqtt.service; + +// import com.bytedesk.socket.mqtt.cache.MqttSubscribeNotWildcardCache; +// import com.bytedesk.socket.mqtt.cache.MqttSubscribeWildcardCache; +// import com.bytedesk.socket.mqtt.model.MqttSubscribe; + +// import lombok.AllArgsConstructor; +// import org.springframework.stereotype.Service; + +// import java.util.ArrayList; +// import java.util.List; + +// /** +// * +// */ +// @Service +// @AllArgsConstructor +// public class MqttSubscribeService { + +// private final MqttSubscribeWildcardCache mqttSubscribeWildcardCache; + +// private final MqttSubscribeNotWildcardCache mqttSubscribeNotWildcardCache; + +// public void put(String topicFilter, MqttSubscribe mqttSubscribe) { +// if (topicFilter.contains("#") || topicFilter.contains("+")) { +// mqttSubscribeWildcardCache.put(topicFilter, mqttSubscribe.getClientId(), mqttSubscribe); +// } else { +// mqttSubscribeNotWildcardCache.put(topicFilter, mqttSubscribe.getClientId(), mqttSubscribe); +// } +// } + +// public void remove(String topicFilter, String clientId) { +// if (topicFilter.contains("#") || topicFilter.contains("+")) { +// mqttSubscribeWildcardCache.remove(topicFilter, clientId); +// } else { +// mqttSubscribeNotWildcardCache.remove(topicFilter, clientId); +// } +// } + +// public void removeForClient(String clientId) { +// mqttSubscribeNotWildcardCache.removeForClient(clientId); +// mqttSubscribeWildcardCache.removeForClient(clientId); +// } + +// public List search(String topic) { +// List subscribeStores = new ArrayList<>(); +// List list = mqttSubscribeNotWildcardCache.all(topic); +// if (!list.isEmpty()) { +// subscribeStores.addAll(list); +// } +// return subscribeStores; +// } +// } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeStoreService.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeStoreService.java deleted file mode 100755 index 71a3fcc5e5..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/service/MqttSubscribeStoreService.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.service; - -import cn.hutool.core.util.StrUtil; -import com.bytedesk.socket.mqtt.model.MqttSubscribe; -import com.bytedesk.socket.mqtt.redis.MqttSubscribeNotWildcardCache; -import com.bytedesk.socket.mqtt.redis.MqttSubscribeWildcardCache; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -// import java.util.Collection; -import java.util.List; - -/** - * 订阅存储服务 - */ -@Service -@AllArgsConstructor -public class MqttSubscribeStoreService { - - private final MqttSubscribeWildcardCache mqttSubscribeWildcardCache; - - private final MqttSubscribeNotWildcardCache mqttSubscribeNotWildcardCache; - - public void put(String topicFilter, MqttSubscribe mqttSubscribe) { - if (StrUtil.contains(topicFilter, '#') || StrUtil.contains(topicFilter, '+')) { - mqttSubscribeWildcardCache.put(topicFilter, mqttSubscribe.getClientId(), mqttSubscribe); - } else { - mqttSubscribeNotWildcardCache.put(topicFilter, mqttSubscribe.getClientId(), mqttSubscribe); - } - } - - public void remove(String topicFilter, String clientId) { - if (StrUtil.contains(topicFilter, '#') || StrUtil.contains(topicFilter, '+')) { - mqttSubscribeWildcardCache.remove(topicFilter, clientId); - } else { - mqttSubscribeNotWildcardCache.remove(topicFilter, clientId); - } - } - - public void removeForClient(String clientId) { - mqttSubscribeNotWildcardCache.removeForClient(clientId); - mqttSubscribeWildcardCache.removeForClient(clientId); - } - - public List search(String topic) { - List subscribeStores = new ArrayList<>(); - List list = mqttSubscribeNotWildcardCache.all(topic); - if (!list.isEmpty()) { - subscribeStores.addAll(list); - } - // TODO: 暂时没用到,注释掉,加快处理速度。待后期需要通配符的时候,放开注释 - // mqttSubscribeWildcardCache.all().forEach((topicFilter, map) -> { - // if (StrUtil.split(topic, '/').size() >= StrUtil.split(topicFilter, - // '/').size()) { - // List splitTopics = StrUtil.split(topic, '/');//a - // List spliteTopicFilters = StrUtil.split(topicFilter, '/');//# - // String newTopicFilter = ""; - // for (int i = 0; i < spliteTopicFilters.size(); i++) { - // String value = spliteTopicFilters.get(i); - // if (value.equals("+")) { - // newTopicFilter = newTopicFilter + "+/"; - // } else if (value.equals("#")) { - // newTopicFilter = newTopicFilter + "#/"; - // break; - // } else { - // newTopicFilter = newTopicFilter + splitTopics.get(i) + "/"; - // } - // } - // newTopicFilter = StrUtil.removeSuffix(newTopicFilter, "/"); - // if (topicFilter.equals(newTopicFilter)) { - // Collection collection = map.values(); - // List list2 = new ArrayList(collection); - // subscribeStores.addAll(list2); - // } - // } - // }); - return subscribeStores; - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttSerializer.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttSerializer.java deleted file mode 100755 index b5e7dc206f..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-29 10:37:50 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.mqtt.util; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.bytedesk.socket.mqtt.model.MqttSession; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.SerializationException; - -/** - * 继承RedisTemplate序列化接口,自定义序列化类 - * - * @author jackning - */ -public class MqttSerializer implements RedisSerializer { - - @Override - public byte[] serialize(Object o) throws SerializationException { - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.create(); - return gson.toJson(o).getBytes(); - } - - @Override - public Object deserialize(byte[] bytes) throws SerializationException { - String s = new String(bytes); - Gson gson = new Gson(); - return gson.fromJson(s, MqttSession.class); - } -} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttUtil.java b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttUtil.java index 355cff3fa0..9eaa2b97c3 100644 --- a/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttUtil.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/mqtt/util/MqttUtil.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-24 11:26:25 + * @LastEditTime: 2024-04-13 15:47:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,7 +14,6 @@ */ package com.bytedesk.socket.mqtt.util; -import cn.hutool.core.util.StrUtil; import io.netty.handler.codec.mqtt.MqttTopicSubscription; import java.io.FileInputStream; @@ -29,23 +28,26 @@ import org.springframework.util.StringUtils; public class MqttUtil { + private MqttUtil() { + } + public static boolean validTopicFilter(List topicSubscriptions) { for (MqttTopicSubscription topicSubscription : topicSubscriptions) { String topicFilter = topicSubscription.topicName(); if (StringUtils.hasText(topicFilter)) { // 以#或+符号开头的、以/符号结尾的订阅按非法订阅处理, 这里没有参考标准协议 - if (StrUtil.startWith(topicFilter, '+') || StrUtil.endWith(topicFilter, '/')) { + if (topicFilter.endsWith("+") || topicFilter.endsWith("/")) { return false; } - if (StrUtil.contains(topicFilter, '#')) { + if (topicFilter.contains("#")) { // 如果出现多个#符号的订阅按非法订阅处理 - if (StrUtil.count(topicFilter, '#') > 1) { + if (StringUtils.countOccurrencesOf(topicFilter, "#") > 1) { return false; } } - if (StrUtil.contains(topicFilter, '+')) { + if (topicFilter.contains("+")) { // 如果+符号和/+字符串出现的次数不等的情况按非法订阅处理 - if (StrUtil.count(topicFilter, '+') != StrUtil.count(topicFilter, "/+")) { + if (StringUtils.countOccurrencesOf(topicFilter, "+") != StringUtils.countOccurrencesOf(topicFilter, "/+")) { return false; } } @@ -81,4 +83,5 @@ public class MqttUtil { return sslContext; } + } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/protobuf/config/ProtobufRedisTemplate.java b/modules/socket/src/main/java/com/bytedesk/socket/protobuf/config/ProtobufRedisTemplate.java deleted file mode 100644 index 24011d4004..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/protobuf/config/ProtobufRedisTemplate.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-04 12:07:11 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.protobuf.config; - -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; -import org.springframework.stereotype.Component; - -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; - -/** - * ProtobufRedisTemplate - */ -@Component -@AutoConfigureAfter(RedisAutoConfiguration.class) -public class ProtobufRedisTemplate extends RedisTemplate { - - public ProtobufRedisTemplate(final LettuceConnectionFactory lettuceConnectionFactory) { - setConnectionFactory(lettuceConnectionFactory); - // 解决key乱码,如:前面还多出了许多类似\xac\xed\x00\x05t\x00这种字符串 - final RedisSerializer stringSerializer = new StringRedisSerializer(); - setKeySerializer(stringSerializer); - setHashKeySerializer(stringSerializer); - afterPropertiesSet(); - } -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/protobuf/util/ProtobufRedisSerializer.java b/modules/socket/src/main/java/com/bytedesk/socket/protobuf/util/ProtobufRedisSerializer.java deleted file mode 100644 index 23692591f0..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/protobuf/util/ProtobufRedisSerializer.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 12:08:21 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.protobuf.util; - -import java.lang.reflect.Method; - -import com.google.protobuf.GeneratedMessageV3; - -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.SerializationException; - -import lombok.Getter; -import lombok.Setter; - -/** - * ProtocbufRedisSerializer - */ -@Setter -@Getter -public class ProtobufRedisSerializer implements RedisSerializer { - - private Class type; - - public ProtobufRedisSerializer(Class type) { - this.type = type; - } - - @Override - public byte[] serialize(Object t) throws SerializationException { - try { - GeneratedMessageV3 gm = (GeneratedMessageV3) t; - return gm.toByteArray(); - } catch (Exception ex) { - throw new SerializationException("Cannot serialize", ex); - } - } - - @SuppressWarnings("unchecked") - @Override - public T deserialize(byte[] bytes) throws SerializationException { - try { - Method method = type.getMethod("parseFrom", new Class[] { bytes.getClass() }); - return (T) method.invoke(type, new Object[] { bytes }); - } catch (Exception ex) { - throw new SerializationException("Cannot deserialize", ex); - } - } -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheFetchService.java b/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheFetchService.java deleted file mode 100644 index a3083aaec8..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheFetchService.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.bytedesk.socket.redis; - -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import com.alibaba.fastjson2.JSONObject; - -import com.bytedesk.socket.protobuf.constant.ProtobufConsts; -import lombok.AllArgsConstructor; -// import lombok.extern.slf4j.Slf4j; - -/** - * 缓存消息 - */ -// @Slf4j -@Service -@AllArgsConstructor -public class RedisMessageCacheFetchService { - - private final StringRedisTemplate stringRedisTemplate; - - // 根据访客端uid - // private static final String MESSAGE_FETCH_PREFIX = - // "bytedeskim:message:fetch:"; - - // 缓存7天-访客uid,供客服端根据访客visitorUid拉取 - public void addMessage(String visitorUid, final String messageJson) { - stringRedisTemplate.opsForSet().add(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid, messageJson); - stringRedisTemplate.expire(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid, 7, TimeUnit.DAYS); - } - - // 消息撤回,从缓存中删除 - public void removeMessage(String visitorUid, String mid) { - Set jsonSet = stringRedisTemplate.opsForSet().members(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - JSONObject jsonObject = JSONObject.parseObject(jsonString); - String midString = jsonObject.getString("mid"); - if (midString.equals(mid)) { - // log.debug("do uid {}, mid {}", uid, mid); - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid, jsonString); - } - } - } - - public Set getMessages(String visitorUid) { - return (Set) stringRedisTemplate.opsForSet().members(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid); - } - - public Long length(String visitorUid) { - return stringRedisTemplate.opsForSet().size(ProtobufConsts.MESSAGE_FETCH_PREFIX + visitorUid); - } - - // 根据topic - // private static final String MESSAGE_TOPIC_PREFIX = - // "bytedeskim:message:fetch:topic:"; - - // 缓存7天 - public void addMessageByTopic(String topic, final String messageJson) { - stringRedisTemplate.opsForSet().add(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic, messageJson); - stringRedisTemplate.expire(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic, 7, TimeUnit.DAYS); - } - - public Set getMessagesByTopic(String topic) { - return (Set) stringRedisTemplate.opsForSet().members(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic); - } - - // 更新缓存中消息已读状态 - public void updateMessageStatusByTopic(String topic, String mid) { - Set jsonSet = stringRedisTemplate.opsForSet().members(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - JSONObject jsonObject = JSONObject.parseObject(jsonString); - String midString = jsonObject.getString("mid"); - if (midString.equals(mid)) { - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic, jsonString); - // - jsonObject.put("status", "read"); - addMessageByTopic(topic, jsonObject.toJSONString()); - } - } - } - - // 消息撤回,从缓存中删除 - public void removeMessageByTopic(String topic, String mid) { - Set jsonSet = stringRedisTemplate.opsForSet().members(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - JSONObject jsonObject = JSONObject.parseObject(jsonString); - String midString = jsonObject.getString("mid"); - if (midString.equals(mid)) { - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic, jsonString); - } - } - } - - public Long lengthTopic(String topic) { - return stringRedisTemplate.opsForSet().size(ProtobufConsts.MESSAGE_TOPIC_PREFIX + topic); - } - - // 客服未读消息 - // private static final String MESSAGE_AGENT_UNREAD_PREFIX = - // "bytedeskim:message:fetch:agentunread:"; - - // 缓存2天-客服未读消息 - public void addMessageAgentUnread(String agentUid, final String messageJson) { - stringRedisTemplate.opsForSet().add(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid, messageJson); - stringRedisTemplate.expire(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid, 2, TimeUnit.DAYS); - } - - public Set getMessagesAgentUnread(String agentUid) { - return (Set) stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid); - } - - public void removeMessageAgentUnread(String agentUid, final String mid) { - // - Set jsonSet = stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - JSONObject jsonObject = JSONObject.parseObject(jsonString); - String midString = jsonObject.getString("mid"); - if (midString.equals(mid)) { - // log.debug("do agentUid {}, mid {}", agentUid, mid); - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid, - jsonString); - } - } - } - - public void clearMessagesAgentUnread(String agentUid) { - // - Set jsonSet = stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - // 删除 - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_AGENT_UNREAD_PREFIX + agentUid, jsonString); - } - } - - // 访客未读消息 - // private static final String MESSAGE_VISITOR_UNREAD_PREFIX = - // "bytedeskim:message:fetch:visitorunread:"; - - // 缓存2天-访客未读消息 - public void addMessageVisitorUnread(String visitorUid, final String messageJson) { - stringRedisTemplate.opsForSet().add(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid, messageJson); - stringRedisTemplate.expire(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid, 2, TimeUnit.DAYS); - } - - public Set getMessagesVisitorUnread(String visitorUid) { - return (Set) stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid); - } - - public void removeMessageVisitorUnread(String visitorUid, final String mid) { - // - Set jsonSet = stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - JSONObject jsonObject = JSONObject.parseObject(jsonString); - String midString = jsonObject.getString("mid"); - if (midString.equals(mid)) { - // log.debug("do visitorUid {}, mid {}", visitorUid, mid); - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid, - jsonString); - } - } - } - - public void clearMessagesVisitorUnread(String visitorUid) { - // - Set jsonSet = stringRedisTemplate.opsForSet() - .members(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid); - Iterator iterator = jsonSet.iterator(); - while (iterator.hasNext()) { - String jsonString = iterator.next(); - // 删除 - stringRedisTemplate.opsForSet().remove(ProtobufConsts.MESSAGE_VISITOR_UNREAD_PREFIX + visitorUid, - jsonString); - } - } - -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheOfflineService.java b/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheOfflineService.java deleted file mode 100644 index de78b0ffbe..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheOfflineService.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-26 10:47:19 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.redis; - -import java.util.concurrent.TimeUnit; -import org.springframework.stereotype.Service; - -import com.bytedesk.socket.protobuf.config.ProtobufRedisTemplate; -import com.bytedesk.socket.protobuf.constant.ProtobufConsts; -import lombok.AllArgsConstructor; -// import lombok.extern.slf4j.Slf4j; - -/** - * 缓存离线消息,客户端重连之后,自动推送到客户端 - */ -// @Slf4j -@Service -@AllArgsConstructor -public class RedisMessageCacheOfflineService { - - private final ProtobufRedisTemplate protobufRedisTemplate; - - // private static final String MESSAGE_OFFLINE_PREFIX = - // "bytedeskim:message:offline:"; - - public void push(String uid, final byte[] bytes) { - protobufRedisTemplate.opsForList().rightPush(ProtobufConsts.MESSAGE_OFFLINE_PREFIX + uid, bytes); - protobufRedisTemplate.expire(ProtobufConsts.MESSAGE_OFFLINE_PREFIX + uid, 7, TimeUnit.DAYS); - } - - public byte[] pop(String uid) { - return (byte[]) protobufRedisTemplate.opsForList().leftPop(ProtobufConsts.MESSAGE_OFFLINE_PREFIX + uid); - } - - public Long length(String uid) { - return protobufRedisTemplate.opsForList().size(ProtobufConsts.MESSAGE_OFFLINE_PREFIX + uid); - } - - public void clear(String uid) { - while (length(uid) > 0) { - pop(uid); - } - } - -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheProtobufService.java b/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheProtobufService.java deleted file mode 100644 index fb8c15d2ed..0000000000 --- a/modules/socket/src/main/java/com/bytedesk/socket/redis/RedisMessageCacheProtobufService.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-01-29 16:21:46 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 14:35:28 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.socket.redis; - -import org.springframework.stereotype.Service; - -import com.bytedesk.socket.protobuf.config.ProtobufRedisTemplate; -import com.bytedesk.socket.protobuf.constant.ProtobufConsts; -import lombok.AllArgsConstructor; -// import lombok.extern.slf4j.Slf4j; - -/** - * 暂时缓存,定时存储到MySQL数据库 - */ -// @Slf4j -@Service -@AllArgsConstructor -public class RedisMessageCacheProtobufService { - - private final ProtobufRedisTemplate protobufRedisTemplate; - - // - public void push(final byte[] bytes) { - protobufRedisTemplate.opsForList().rightPush(ProtobufConsts.MESSAGE_PREFIX, bytes); - } - - public byte[] pop() { - return (byte[]) protobufRedisTemplate.opsForList().leftPop(ProtobufConsts.MESSAGE_PREFIX); - } - - public Long length() { - return protobufRedisTemplate.opsForList().size(ProtobufConsts.MESSAGE_PREFIX); - } - - public void clear() { - while (length() > 0) { - pop(); - } - } -} \ No newline at end of file diff --git a/modules/socket/src/main/java/com/bytedesk/socket/service/MessageJsonService.java b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageJsonService.java new file mode 100644 index 0000000000..a2f1afaa81 --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageJsonService.java @@ -0,0 +1,109 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-16 18:04:37 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 23:04:54 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.service; + +import org.modelmapper.ModelMapper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.thread.Thread; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.message.Message; +import com.bytedesk.core.message.MessageResponse; +import com.bytedesk.core.message.MessageService; +import com.bytedesk.core.thread.ThreadService; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class MessageJsonService { + + private final MessageService messageService; + + private final ThreadService threadService; + + private final ModelMapper modelMapper; + + @Async + public void saveToDb(String messageJSON) { + log.info("saveToDb: {}", messageJSON); + MessageResponse messageResponse = JSON.parseObject(messageJSON, MessageResponse.class); + // + String type = messageResponse.getType(); + // + dealWithMessageReceipt(type, messageResponse); + + dealWithMessageRecall(type, messageResponse); + // + String mid = messageResponse.getMid(); + if (messageService.existsByMid(mid)) { + log.info("message already exists, mid: {}", mid); + return; + } + // + Thread thread = getThread(messageResponse); + if (thread == null) { + log.info("thread not exists, tid: {}", messageResponse.getThread().getTid()); + return; + } + + Message message = getMessage(thread, messageResponse); + + threadService.save(thread); + + messageService.save(message); + + log.info("save json msg mid {}, tid {}", message.getMid(), thread.getTid()); + } + + private Thread getThread(MessageResponse messageResponse) { + + String tid = messageResponse.getThread().getTid(); + Thread thread = threadService.findByTid(tid).orElse(null); + if (thread == null) { + log.info("thread not exists, tid: {}", tid); + return null; + } + thread.setContent(messageResponse.getContent()); + + return thread; + } + + private Message getMessage(Thread thread, MessageResponse messageResponse) { + + Message message = modelMapper.map(messageResponse, Message.class); + message.setStatus(StatusConsts.MESSAGE_STATUS_STORED); + message.getThreads().add(thread); + message.setUser(JSON.toJSONString(messageResponse.getUser())); + message.setOrgOid(thread.getOrgOid()); + + return message; + } + + // 处理消息回执 + private void dealWithMessageReceipt(String type, MessageResponse message) { + log.info("dealWithMessageReceipt: {}", type); + } + + // 消息撤回,从数据库中删除消息 + private void dealWithMessageRecall(String type, MessageResponse message) { + log.info("dealWithMessageRecall: {}", type); + } + +} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/service/MessageProtoService.java b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageProtoService.java new file mode 100644 index 0000000000..1ab6ffe13b --- /dev/null +++ b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageProtoService.java @@ -0,0 +1,182 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-16 18:02:11 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-22 23:06:16 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.socket.service; + +import org.springframework.lang.NonNull; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSON; +import com.bytedesk.core.constant.StatusConsts; +import com.bytedesk.core.constant.ThreadTypeConsts; +import com.bytedesk.core.message.MessageService; +import com.bytedesk.core.rbac.user.UserResponseSimple; +import com.bytedesk.core.thread.ThreadService; +import com.bytedesk.socket.protobuf.model.MessageProto; +import com.bytedesk.socket.protobuf.model.ThreadProto; +import com.bytedesk.socket.protobuf.model.UserProto; +import com.google.protobuf.InvalidProtocolBufferException; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import com.bytedesk.core.thread.Thread; +import com.bytedesk.core.message.Message; + +@Slf4j +@Service +@AllArgsConstructor +public class MessageProtoService { + + private final MessageService messageService; + + private final ThreadService threadService; + + @Async + public void saveToDb(@NonNull byte[] messageBytes) { + // + try { + MessageProto.Message messageProto = MessageProto.Message.parseFrom(messageBytes); + // + String type = messageProto.getType(); + log.info("saveToDb {}", type); + + // 处理消息回执 + dealWithMessageReceipt(type, messageProto); + + // 消息撤回,从数据库中删除消息 + dealWithMessageRecall(type, messageProto); + + // 流式输出,根据mid替换掉旧消息内容content + // dealWithMessageStream(messageProto); + + // 消息所属的会话 + Thread thread = getThread(messageProto); + if (thread == null) { + log.info("thread not exists, tid: {}", messageProto.getThread().getTid()); + return; + } + + Message message = getMessage(thread, messageProto); + + threadService.save(thread); + // + messageService.save(message); + + log.info("save proto msg mid {}, tid {}", message.getMid(), thread.getTid()); + + // 以json格式存储到message archive里面,方便搜索 + // String messageJson = ProtobufJsonUtil.toJson(messageProto); + // messageArchiveService.save(messageJson, type, sendUser.getSubDomain()); + + } catch (InvalidProtocolBufferException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + private Thread getThread(MessageProto.Message messageProto) { + + ThreadProto.Thread threadProto = messageProto.getThread(); + String tid = threadProto.getTid(); + // log.info("tid: {}, threadType {}", tid, threadType); + Thread thread = threadService.findByTid(tid).orElse(null); + if (thread == null) { + log.info("thread not exists, tid: {}", tid); + return null; + } + // thread.setContent(threadProto.getContent()); + return thread; + } + + private Message getMessage(Thread thread, MessageProto.Message messageProto) { + // + String type = messageProto.getType(); + // 消息发送者 + UserProto.User userProto = messageProto.getUser(); + String uid = userProto.getUid(); + String avatar = userProto.getAvatar(); + String nickname = userProto.getNickname(); + log.debug("uid: {}, avatar {}, nickname {}", uid, nickname, avatar); + // + String mid = messageProto.getMid(); + // 持久化消息 + Message message = new Message(); + message.setMid(mid); + message.setType(type); + message.setStatus(StatusConsts.MESSAGE_STATUS_STORED); + message.setClient(messageProto.getClient()); + // + UserResponseSimple user = UserResponseSimple.builder().uid(uid).nickname(nickname).avatar(avatar).build(); + message.setUser(JSON.toJSONString(user)); + // message.setThread(thread); + message.getThreads().add(thread); + if (thread.getType().equals(ThreadTypeConsts.MEMBER)) { + Thread reverseThread = threadService.getReverse(thread); + message.getThreads().add(reverseThread); + } + // + // 设置为消息体内的时间戳,但是数据库中还是存储由@CreatedDate生成的时间戳 + // message.setCreatedAt(BdDateUtils.formatStringToDateTime(messageProto.getCreatedAt())); + // message.setUpdatedAt(BdDateUtils.formatStringToDateTime(messageProto.getCreatedAt())); + // 暂定:所有消息类型均放到text里面处理,复杂类型存储json + String content = messageProto.getContent(); + message.setContent(content); + message.setOrgOid(thread.getOrgOid()); + // + return message; + } + + + /** + * 处理消息回执 + * + * @param type + * @param messageProto + */ + private void dealWithMessageReceipt(String type, MessageProto.Message messageProto) { + + } + + /** + * 消息撤回,从数据库中删除消息 + * + * @param type + * @param messageProto + */ + private void dealWithMessageRecall(String type, MessageProto.Message messageProto) { + // + } + + /** + * 流式输出,根据mid替换掉旧消息内容content + * + * @param type + * @param messageProto + */ + // private void dealWithMessageStream(MessageProto.Message messageProto) { + // String mid = messageProto.getMid(); + // Optional messageOptional = messageService.findByMid(mid); + // if (messageOptional.isPresent()) { + // // + // String content = messageProto.getContent(); + // messageOptional.get().setContent(content); + // messageService.save(messageOptional.get()); + // return; + // } + // } + +} diff --git a/modules/socket/src/main/java/com/bytedesk/socket/service/MessageSocketService.java b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageSocketService.java index 504c142ad6..2b3177ebd1 100644 --- a/modules/socket/src/main/java/com/bytedesk/socket/service/MessageSocketService.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/service/MessageSocketService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-26 10:36:50 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-04 15:53:36 + * @LastEditTime: 2024-04-23 16:12:07 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,38 +14,26 @@ */ package com.bytedesk.socket.service; -import java.util.List; -import java.util.Optional; +import java.util.Set; -import org.springframework.lang.NonNull; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.bytedesk.core.constant.MessageTypeConsts; import com.bytedesk.core.constant.MqConsts; -import com.bytedesk.core.constant.StatusConsts; import com.bytedesk.core.constant.ThreadTypeConsts; -import com.bytedesk.core.message.Message; -import com.bytedesk.core.message.MessageService; -import com.bytedesk.core.rbac.user.User; -import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.redis.RedisUserService; -import com.bytedesk.core.thread.Thread; -import com.bytedesk.core.thread.ThreadService; -import com.bytedesk.core.utils.BdDateUtils; -import com.bytedesk.socket.mqtt.model.MqttSubscribe; -import com.bytedesk.socket.mqtt.service.MqttClientIdStoreService; +import com.bytedesk.core.message.MessageResponse; +import com.bytedesk.core.topic.Topic; +import com.bytedesk.core.topic.TopicService; +import com.bytedesk.socket.mqtt.model.MqttSession; +// import com.bytedesk.socket.mqtt.model.MqttSubscribe; +// import com.bytedesk.socket.mqtt.service.MqttClientIdService; import com.bytedesk.socket.mqtt.service.MqttMessageIdService; -import com.bytedesk.socket.mqtt.service.MqttSessionStoreService; -import com.bytedesk.socket.mqtt.service.MqttSubscribeStoreService; +import com.bytedesk.socket.mqtt.service.MqttSessionService; +// import com.bytedesk.socket.mqtt.service.MqttSubscribeService; import com.bytedesk.socket.protobuf.model.MessageProto; import com.bytedesk.socket.protobuf.model.ThreadProto; -import com.bytedesk.socket.protobuf.model.UserProto; -import com.google.protobuf.InvalidProtocolBufferException; - import io.netty.buffer.Unpooled; import io.netty.handler.codec.mqtt.MqttFixedHeader; import io.netty.handler.codec.mqtt.MqttMessageFactory; @@ -68,24 +56,9 @@ public class MessageSocketService { private final MqttMessageIdService mqttMessageIdService; - private final MqttSessionStoreService mqttSessionStoreService; + private final MqttSessionService mqttSessionService; - private final MqttSubscribeStoreService mqttSubscribeStoreService; - - // private final MqttDupPublishMessageStoreService - // mqttDupPublishMessageStoreService; - - private final MqttClientIdStoreService mqttClientIdStoreService; - - // private final EventPublisher eventPublisher; - - private final RedisUserService redisUserService; - - private final MessageService messageService; - - private final ThreadService threadService; - - private final UserService userService; + private final TopicService topicService; public void sendJsonMessage(String messageJSON) { log.debug("send json message {}", messageJSON); @@ -93,75 +66,33 @@ public class MessageSocketService { if (!StringUtils.hasLength(messageJSON)) { return; } - // - try { - // - JSONObject messageObject = JSON.parseObject(messageJSON); - // - JSONObject topicObject; - String messageType = messageObject.getString("type"); - // - if (messageType.equals(MessageTypeConsts.TEXT) || messageType.equals(MessageTypeConsts.IMAGE)) { - // 一般消息 - topicObject = messageObject.getJSONObject("thread"); - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER) - || messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER_ACCEPT) - || messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER_REJECT)) { - // 会话转接 - topicObject = messageObject.getJSONObject("transfer"); - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE) - || messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE_ACCEPT) - || messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE_REJECT)) { - // 会话邀请 - topicObject = messageObject.getJSONObject("invite"); - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_NOTICE)) { - // 非会话消息,如:会议通知等 - topicObject = messageObject.getJSONObject("notice"); - } else { - // 一般消息 - topicObject = messageObject.getJSONObject("thread"); - } - // - String jsonTopic = topicObject.getString("topic"); - if (jsonTopic != null) { - // stomp客户端topic需要以'topic/'为前缀,故在发送给stomp客户端之前,添加此前缀 - // stomp客户端以'.'为多级分隔符,mqtt客户端以'/'为多级分隔符,故在发送给stomp客户端之前,需要替换掉'/' - String topic = MqConsts.TOPIC_PREFIX + jsonTopic.replace("/", "."); - log.debug("stomp topic {}", topic); - // 发送给Stomp客户端 - simpMessagingTemplate.convertAndSend(topic, messageJSON); - } - - } catch (Exception e) { - log.error(e.toString()); - e.printStackTrace(); - } + // + MessageResponse messageObject = JSON.parseObject(messageJSON, MessageResponse.class); + String topic = MqConsts.TOPIC_PREFIX + messageObject.getThread().getTopic().replace("/", "."); + log.debug("stomp topic {}", topic); + // 发送给Stomp客户端 + simpMessagingTemplate.convertAndSend(topic, messageJSON); } public void sendProtoMessage(MessageProto.Message messageProto) { log.debug("send proto message"); // // 广播给消息发送者的多个客户端,如:pc客户端发送消息,手机客户端可以同步收到自己发送的消息, 群组会话除外 - String messageType = messageProto.getType(); - String threadType = messageProto.getThread().getType(); byte[] messageBytes = messageProto.toByteArray(); + String threadType = messageProto.getThread().getType(); // 发送消息给订阅者 // 只有contact会话需要替换tid/topic/nickname/avatar + // 广播给消息接收者,一对一会话的tid互为翻转 if (threadType.equals(ThreadTypeConsts.MEMBER)) { - // 广播给消息接收者,一对一会话的tid互为翻转 doSendToSenderClients(messageProto); // 广播给消息发送者的多个客户端,如:pc客户端发送消息,手机客户端可以同步收到自己发送的消息 String tid = messageProto.getThread().getTid(); String reverseTid = new StringBuffer(tid).reverse().toString(); // - String userNickname = messageProto.getUser().getNickname(); - String userAvatar = messageProto.getUser().getAvatar(); String userTopic = messageProto.getUser().getUid(); ThreadProto.Thread thread = messageProto.getThread().toBuilder() .setTid(reverseTid) - .setNickname(userNickname) - .setAvatar(userAvatar) .setTopic(userTopic) .build(); MessageProto.Message message = messageProto.toBuilder() @@ -169,372 +100,65 @@ public class MessageSocketService { .build(); messageBytes = message.toByteArray(); } - - // 转接会话 - if (messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER) - || messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER_ACCEPT) - || messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER_REJECT)) { - // 广播给消息发送者的多个客户端,如:pc客户端发送消息,手机客户端可以同步收到自己发送的消息 - doSendToSenderClients(messageProto); - // 会话转接 - String transferTopic = messageProto.getTransfer().getTopic(); - // log.debug("doSendMqttProtobufMessage transferTopic {}", transferTopic); - doSendToSubscribers(transferTopic, messageBytes); - // TODO: 如果是accept的话,在数据库中修改thread的agent - if (messageType.equals(MessageTypeConsts.NOTIFICATION_TRANSFER_ACCEPT)) { - // - String topic = messageProto.getThread().getTopic(); - String subscriberUid = messageProto.getUser().getUid(); - String unSubscriberUid = messageProto.getTransfer().getTopic(); - // 如果是accept的话,接受者这添加订阅,被接受者取消订阅 - subscribe(subscriberUid, topic); - unsubscribe(unSubscriberUid, topic); - // 发送事件通知,在其他地方更新数据库 - // FIXME: 多个服务器实例,会更新多次,但不影响正确性 - // eventPublisher.publishThreadTransferAccept(messageProto); - } - // - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE) - || messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE_ACCEPT) - || messageType.equals(MessageTypeConsts.NOTIFICATION_INVITE_REJECT)) { - // 广播给消息发送者的多个客户端,如:pc客户端发送消息,手机客户端可以同步收到自己发送的消息, 群组会话除外 - doSendToSenderClients(messageProto); - // 会话邀请 - String inviteTopic = messageProto.getInvite().getTopic(); - doSendToSubscribers(inviteTopic, messageBytes); - // - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_GROUP_CREATE)) { - // 创建群组,给群成员添加订阅群组topic - String topic = messageProto.getThread().getTopic(); - String uids = messageProto.getExtra().getContent(); - String[] uidList = uids.split(","); - for (String uid : uidList) { - subscribe(uid, topic); - } - // - doSendToSubscribers(topic, messageBytes); - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_NOTICE)) { - // 非会话消息,如:会议通知等 - String topic = messageProto.getNotice().getTopic(); - doSendToSubscribers(topic, messageBytes); - } else if (messageType.equals(MessageTypeConsts.NOTIFICATION_QUEUE_ACCEPT)) { - // - String topic = messageProto.getThread().getTopic(); - String subscriberUid = messageProto.getUser().getUid(); - // 给接入客服添加订阅 - subscribe(subscriberUid, topic); - doSendToSubscribers(topic, messageBytes); - } else { - // - String topic = messageProto.getThread().getTopic(); - log.debug("mqtt send topic {}", topic); - doSendToSubscribers(topic, messageBytes); - } + // + String topic = messageProto.getThread().getTopic(); + log.debug("mqtt send topic {}", topic); + doSendToSubscribers(topic, messageBytes); } private void doSendToSubscribers(String topic, byte[] messageBytes) { + // String sid = topic.split("/")[0]; log.debug("doSendToSubscribers {}", topic); // - try { - // - MqttQoS mqttQoS = MqttQoS.AT_LEAST_ONCE; - boolean dup = false; - boolean retain = false; - // 内存订阅信息 - List subscribeStores = mqttSubscribeStoreService.search(topic); - subscribeStores.forEach(subscribeStore -> { - // - String clientId = subscribeStore.getClientId(); - // 当前活跃长连接信息 - if (mqttSessionStoreService.containsKey(clientId)) { - // 订阅者收到MQTT消息的QoS级别, 最终取决于发布消息的QoS和主题订阅的QoS - // MqttQoS respQoS = mqttQoS.value() > subscribeStore.getMqttQoS() ? - // MqttQoS.valueOf(subscribeStore.getMqttQoS()) : mqttQoS; - MqttQoS respQoS = mqttQoS; - int messageId = mqttMessageIdService.getNextMessageId(); - // - MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( - new MqttFixedHeader(MqttMessageType.PUBLISH, dup, respQoS, retain, 0), - new MqttPublishVariableHeader(topic, messageId), - Unpooled.buffer().writeBytes(messageBytes)); - mqttSessionStoreService.get(clientId).getChannel().writeAndFlush(publishMessage); - // log.debug("topic {} clientId {}", topic, clientId); - // - // if (respQoS == MqttQoS.AT_LEAST_ONCE || respQoS == MqttQoS.EXACTLY_ONCE) { - // MqttDupPublishMessage dupPublishMessageStore = new MqttDupPublishMessage() - // .setClientId(clientId) - // .setTopic(topic) - // .setMqttQoS(respQoS.value()) - // .setMessageBytes(messageBytes) - // .setMessageId(messageId); - // mqttDupPublishMessageStoreService.put(clientId, dupPublishMessageStore); - // } - } + Set topicSet = topicService.findByTopic(topic); + log.info("topicList size {}", topicSet.size()); + topicSet.forEach(topicElement -> { + Set clientIdSet = topicElement.getClientIds(); + clientIdSet.forEach(clientId -> { + doSendMessage(topic, messageBytes, clientId); }); - - } catch (Exception e) { - e.printStackTrace(); - } + }); } private void doSendToSenderClients(MessageProto.Message messageProto) { + log.debug("doSendToSenderClients"); // String uid, String topic, byte[] messageBytes - String uid = messageProto.getUser().getUid(); + // String uid = messageProto.getUser().getUid(); String topic = messageProto.getThread().getTopic(); byte[] messageBytes = messageProto.toByteArray(); - // - List clientIdList = mqttClientIdStoreService.get(uid); - clientIdList.forEach(clientId -> { - log.debug("doSendToSenderClients clientId {}", clientId); + // + // String sid = topic.split("/")[0]; + // List topicList = topicService.findByTopic(sid); + Set topicSet = topicService.findByTopic(topic); + topicSet.forEach(topicElement -> { + Set clientIdList = topicElement.getClientIds(); + clientIdList.forEach(clientId -> { + doSendMessage(topic, messageBytes, clientId); + }); + }); + } + + private void doSendMessage(String topic, byte[] messageBytes, String clientId) { + log.debug("doSendMessage: {}, {}", topic, clientId); + // + MqttQoS mqttQoS = MqttQoS.AT_LEAST_ONCE; + boolean dup = false; + boolean retain = false; + // 当前活跃长连接信息 + if (mqttSessionService.containsKey(clientId)) { + log.debug("hasSession: topic {} clientId {}", topic, clientId); + // 订阅者收到MQTT消息的QoS级别, 最终取决于发布消息的QoS和主题订阅的QoS int messageId = mqttMessageIdService.getNextMessageId(); + // MqttPublishMessage publishMessage = (MqttPublishMessage) MqttMessageFactory.newMessage( - new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0), + new MqttFixedHeader(MqttMessageType.PUBLISH, dup, mqttQoS, retain, 0), new MqttPublishVariableHeader(topic, messageId), Unpooled.buffer().writeBytes(messageBytes)); - mqttSessionStoreService.get(clientId).getChannel().writeAndFlush(publishMessage); - }); - } - - private void subscribe(String uid, String topic) { - log.debug("subscribe uid {}, topic {}", uid, topic); - redisUserService.addTopic(uid, topic); - // - MqttQoS qoS = MqttQoS.AT_LEAST_ONCE; - List clientIdList = mqttClientIdStoreService.get(uid); - clientIdList.forEach(clientId -> { - log.debug("subscribe clientId {}", clientId); - final MqttSubscribe subscribeStore = new MqttSubscribe(clientId, topic, qoS.value()); - mqttSubscribeStoreService.put(topic, subscribeStore); - }); - } - - private void unsubscribe(String uid, String topic) { - log.debug("unsubscribe uid {}, topic {}", uid, topic); - redisUserService.removeTopic(uid, topic); - // - List clientIdList = mqttClientIdStoreService.get(uid); - clientIdList.forEach(clientId -> { - log.debug("unsubscribe clientId {}", clientId); - mqttSubscribeStoreService.remove(topic, clientId); - }); - } - - public void saveToDb(@NonNull byte[] messageBytes) { - // - try { - MessageProto.Message messageProto = MessageProto.Message.parseFrom(messageBytes); - // - String type = messageProto.getType(); - log.info("saveToDb {}", type); - - // 处理消息回执 - dealWithMessageReceipt(type, messageProto); - - // 消息撤回,从数据库中删除消息 - dealWithMessageRecall(type, messageProto); - - // 流式输出,根据mid替换掉旧消息内容content - dealWithMessageStream(messageProto); - - // 消息所属的会话 - Thread thread = getThread(messageProto); - - Message message = getMessage(thread, messageProto); - - threadService.save(thread); - // - messageService.save(message); - - log.info("save msg mid {}, tid {}", message.getMid(), thread.getTid()); - - // 以json格式存储到message archive里面,方便搜索 - // String messageJson = ProtobufJsonUtil.toJson(messageProto); - // messageArchiveService.save(messageJson, type, sendUser.getSubDomain()); - - } catch (InvalidProtocolBufferException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // + final MqttSession mqttSession = mqttSessionService.get(clientId); + mqttSession.getChannel().writeAndFlush(publishMessage); + } } - - /** - * 处理消息回执 - * - * @param type - * @param messageProto - */ - private void dealWithMessageReceipt(String type, MessageProto.Message messageProto) { - // - if (type.equals(MessageTypeConsts.NOTIFICATION_RECEIPT)) { - // receipt消息状态,更新到数据库 - String receiptMid = messageProto.getReceipt().getMid(); - String status = messageProto.getReceipt().getStatus(); - // - Optional messageOptional = messageService.findByMid(receiptMid); - if (messageOptional.isPresent() - && !messageOptional.get().getStatus().equals(StatusConsts.MESSAGE_STATUS_READ)) { - // 更新非read状态 - messageOptional.get().setStatus(status); - messageService.save(messageOptional.get()); - } - // 收到已读回执,删除缓存 - if (status.equals(StatusConsts.MESSAGE_STATUS_READ)) { - // 更新缓存中已读状态 - // String topic = messageProto.getThread().getTopic(); - // redisMessageCacheFetchService.updateMessageStatusByTopic(topic, receiptMid); - // // 会话tid - // String threadTid = messageProto.getThread().getTid(); - // // 客服uid - // String agentUid = redisThreadService.getThreadAgentMap(threadTid); - // // 发送消息者的uid - // String senderUid = messageProto.getUser().getUid(); - // // 如果不是客服自己发送的消息 - // if (!senderUid.equals(agentUid)) { - // // 访客发送的消息。收到receipt之后,会从客服缓存中删除 - // redisMessageCacheFetchService.removeMessageAgentUnread(agentUid, receiptMid); - // } else { - // String[] tids = threadTid.split("_"); - // if (tids.length > 1) { - // // 访客uid - // String visitorUid = tids[1]; - // // 客服发送的消息。收到receipt之后,会从访客缓存中删除 - // redisMessageCacheFetchService.removeMessageVisitorUnread(visitorUid, - // receiptMid); - // } - // } - } - return; - } - - } - - /** - * 消息撤回,从数据库中删除消息 - * - * @param type - * @param messageProto - */ - private void dealWithMessageRecall(String type, MessageProto.Message messageProto) { - // - if (type.equals(MessageTypeConsts.NOTIFICATION_RECALL)) { - String recallMid = messageProto.getRecall().getMid(); - messageService.deleteByMid(recallMid); - // 会话tid - String threadTid = messageProto.getThread().getTid(); - String[] tids = threadTid.split("_"); - if (tids.length > 1) { - // 访客uid - // String visitorUid = tids[1]; - /// 从缓存中删除 - // redisMessageCacheFetchService.removeMessage(visitorUid, recallMid); - } - // 会话topic - // String topic = messageProto.getThread().getTopic(); - // redisMessageCacheFetchService.removeMessageByTopic(topic, recallMid); - return; - } - - } - - /** - * 流式输出,根据mid替换掉旧消息内容content - * - * @param type - * @param messageProto - */ - private void dealWithMessageStream(MessageProto.Message messageProto) { - String mid = messageProto.getMid(); - Optional messageOptional = messageService.findByMid(mid); - if (messageOptional.isPresent()) { - // - String content = messageProto.getText().getContent(); - messageOptional.get().setContent(content); - messageService.save(messageOptional.get()); - return; - } - } - - private Thread getThread(MessageProto.Message messageProto) { - - ThreadProto.Thread threadProto = messageProto.getThread(); - String tid = threadProto.getTid(); - // log.info("tid: {}, threadType {}", tid, threadType); - Thread thread = threadService.findByTid(tid).orElse(null); - thread.setContent(threadProto.getContent()); - return thread; - } - - private Message getMessage(Thread thread, MessageProto.Message messageProto) { - // - String type = messageProto.getType(); - // 消息发送者 - UserProto.User userProto = messageProto.getUser(); - String senderUid = userProto.getUid(); - log.info("uid: {}", senderUid); - User user = userService.findByUid(senderUid).orElse(null); - // - String mid = messageProto.getMid(); - // 持久化消息 - Message message = new Message(); - message.setMid(mid); - message.setType(type); - message.setStatus(StatusConsts.MESSAGE_STATUS_STORED); - message.setClient(messageProto.getClient()); - message.setUser(user); - // message.setThread(thread); - message.getThreads().add(thread); - if (thread.getType().equals(ThreadTypeConsts.MEMBER)) { - Thread reverseThread = threadService.getReverse(thread); - message.getThreads().add(reverseThread); - } - // - // 设置为消息体内的时间戳,但是数据库中还是存储由@CreatedDate生成的时间戳 - message.setCreatedAt(BdDateUtils.formatStringToDateTime(messageProto.getTimestamp())); - message.setUpdatedAt(BdDateUtils.formatStringToDateTime(messageProto.getTimestamp())); - // 暂定:所有消息类型均放到text里面处理,复杂类型存储json - String content = messageProto.getText().getContent(); - message.setContent(content); - // switch (type) { - // case MessageTypeConsts.TEXT: - // log.info("message type {}", type); - // String content = messageProto.getText().getContent(); - // message.setContent(content); - // break; - // case MessageTypeConsts.IMAGE: - // // - // String imageUrl = messageProto.getImage().getImageUrl(); - // message.setContent(imageUrl); - // break; - // case MessageTypeConsts.FILE: - // // - // String fileUrl = messageProto.getFile().getFileUrl(); - // message.setContent(fileUrl); - // break; - // case MessageTypeConsts.VOICE: - // // - // String vioceUrl = messageProto.getVoice().getVoiceUrl(); - // message.setContent(vioceUrl); - // break; - // case MessageTypeConsts.VIDEO: - // // - // String videoUrl = messageProto.getVideo().getVideoOrShortUrl(); - // message.setContent(videoUrl); - // break; - // case MessageTypeConsts.COMMODITY: - // String commodity = messageProto.getText().getContent(); - // message.setContent(commodity); - // break; - // default: - // log.info("message other type {}", type); - // String otherContent = messageProto.getText().getContent(); - // if (!StringUtils.hasLength(otherContent)) { - // otherContent = messageProto.getExtra().getContent(); - // } - // message.setContent(otherContent); - // break; - // } - // - return message; - } - + } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/stomp/controller/StompController.java b/modules/socket/src/main/java/com/bytedesk/socket/stomp/controller/StompController.java index 93206f2e5f..047fb849cd 100644 --- a/modules/socket/src/main/java/com/bytedesk/socket/stomp/controller/StompController.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/stomp/controller/StompController.java @@ -1,3 +1,17 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:21:46 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-16 16:51:36 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.socket.stomp.controller; import lombok.AllArgsConstructor; @@ -7,15 +21,11 @@ import org.springframework.lang.NonNull; // import org.springframework.messaging.Message; import org.springframework.messaging.handler.annotation.*; import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.messaging.simp.annotation.SendToUser; -import org.springframework.messaging.simp.annotation.SubscribeMapping; import org.springframework.stereotype.Controller; import com.bytedesk.socket.stomp.service.StompMqService; import java.security.Principal; -import java.util.List; -import java.util.ArrayList; /** * https://docs.spring.io/spring-framework/reference/web/websocket/stomp/handle-annotations.html @@ -32,19 +42,29 @@ public class StompController { private final SimpMessagingTemplate simpMessagingTemplate; /** - * 一对一聊天 - * + * stompClient.publish('/app/sid.uid', message) + * 访客端发送消息 + * * @param principal principal - * @param message msg + * @param sid agent.uid or workgroup.wid + * @param uid visitor.uid + * @param message content */ - // @MessageMapping("/message") - // public void message(Principal principal, Message message) { - // log.debug("one message from: {} content: {}", principal.getName(), - // message.toString()); - // // 消息原路返回 - // simpMessagingTemplate.convertAndSendToUser(principal.getName(), - // "/queue/message", message); - // } + @MessageMapping("/{sid}.{uid}") + public void message(Principal principal, + @DestinationVariable(value = "sid") String sid, + @DestinationVariable(value = "uid") String uid, + String message) { + log.debug("principal: {}, sid: {}, uid: {}, message: {}", principal, sid, uid, message); + // MessageResponse messageResponse = JSON.parseObject(message, MessageResponse.class); + // // 发送回执 + // JSONObject ackObject = new JSONObject(); + // ackObject.put("type", MessageTypeConsts.NOTIFICATION_ACK_SUCCESS); + // ackObject.put("mid", messageResponse.getMid()); + // simpMessagingTemplate.convertAndSend(MqConsts.TOPIC_PREFIX + sid + '.' + uid, ackObject); + // 转发给mq + stompMqService.sendMessageToMq(message); + } /** * 测试发布消息接口 @@ -63,77 +83,4 @@ public class StompController { simpMessagingTemplate.convertAndSend("/topic/test." + topic, message); } - /** - * 发送普通客服消息 - * - * @param principal principal - * @param wid wid - * @param uid uid - * @param message msg - */ - @MessageMapping("/{wid}.{uid}") - public void message(Principal principal, - @DestinationVariable(value = "wid") String wid, - @DestinationVariable(value = "uid") String uid, - String message) { - // TODO: 发送回执 - log.debug("message: {}, {}, {}", wid, uid, message); - // 转发给mq - stompMqService.sendMessageToMq(message); - } - - /** - * 订阅会话id,返回会话中的用户 - * 注意: 不能监听订阅 /topic/ - * - * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-stomp-subscribe-mapping - * - * @param principal principal - * @param threadId tid - * @return list - */ - @SubscribeMapping("/thread.{threadId}") - public List subscribeThread(Principal principal, - @DestinationVariable(value = "threadId") String threadId) { - log.debug("subscribe from: " + principal.getName() + " threadId:" + threadId); - // TODO: 从数据库中查询并返回 - return new ArrayList<>(); - } - - /** - * 点对点推送 - * - * @param text text - * @param sessionId sessionId - * @return string - * @throws Exception - */ - @MessageMapping(value = "/speak") - @SendToUser(value = "/personal") - public String speak(@Payload String text, - @Header("simpSessionId") String sessionId) throws Exception { - log.debug("收到私人消息:" + text); - return text; - } - - /** - * 异常信息推送 - * - * broadcast=false 说明: - * If the user has more than one session, by default all of the sessions - * subscribed to the given destination are targeted. - * However sometimes, it may be necessary to target only the session that sent - * the message being handled. - * This can be done by setting the broadcast - * - * @param exception - * @return - */ - @MessageExceptionHandler - @SendToUser(destinations = "/queue/errors", broadcast = false) - public String handleException(Throwable exception) { - exception.printStackTrace(); - return exception.getMessage(); - } - } diff --git a/modules/socket/src/main/java/com/bytedesk/socket/stomp/service/StompMqService.java b/modules/socket/src/main/java/com/bytedesk/socket/stomp/service/StompMqService.java index 0a4cd723a7..ea7dec4b28 100644 --- a/modules/socket/src/main/java/com/bytedesk/socket/stomp/service/StompMqService.java +++ b/modules/socket/src/main/java/com/bytedesk/socket/stomp/service/StompMqService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:21:46 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-28 11:56:40 + * @LastEditTime: 2024-04-11 17:37:01 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,12 +17,12 @@ package com.bytedesk.socket.stomp.service; import org.springframework.stereotype.Service; import com.bytedesk.core.event.BytedeskEventPublisher; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; /** * */ -@Slf4j +// @Slf4j @Service @AllArgsConstructor public class StompMqService { @@ -30,7 +30,8 @@ public class StompMqService { private final BytedeskEventPublisher bytedeskEventPublisher; public void sendMessageToMq(String json) { - log.debug("sendMessageToMq: {}", json); + // log.debug("sendMessageToMq: {}", json); + // bytedeskEventPublisher.publishMessageJsonEvent(json); } diff --git a/modules/socket/src/main/proto/message.proto b/modules/socket/src/main/proto/message.proto index de0a06390e..6a9ce5b0ee 100644 --- a/modules/socket/src/main/proto/message.proto +++ b/modules/socket/src/main/proto/message.proto @@ -36,188 +36,23 @@ import "thread.proto"; // 会话类型, 将会话类型作为topic前缀来区分,如:agent/...., user/..., group/... -// 消息体内容 -message Text { - // 文本消息内容 - string content = 1; -} - -message Image { - // - string mediaId = 1; - // 图片消息, 微信pic_url,web版容易引起跨域访问问题,所以要使用image_url - string picUrl = 2; - // 存储在自己服务器之后的url - string imageUrl = 3; -} - -message File { - // 文件消息类型,文件url, 文件类型通过format标示 - string fileUrl = 1; - // 文件名 - string fileName = 2; - // 文件大小 - string fileSize = 3; -} - -message Voice { - // 语音消息,图片+语音+视频+短视频 公用字段 - string mediaId = 1; - // 语音格式amr等 - string format = 2; - // 语音url - string voiceUrl = 3; - // 语音长度 -// google.protobuf.Int32Value length = 4; - int32 length = 4; - // 是否已经播放过 - bool played = 5; -} - -message Video { - // - string mediaId = 1; - // 视频和短视频 - string thumbMediaId = 2; - // - string videoOrShortUrl = 3; - // - string videoOrShortThumbUrl = 4; - // - string title = 5; - // - string description = 6; -} - -message Location { - // 地理位置消息 - string locationX = 1; - // - string locationY = 2; - // - string scale = 3; - // - string label = 4; -} - -message Link { - // 链接消息 - string title = 1; - // - string description = 2; - // - string url = 3; -} - -message Receipt { - // 消息回执 - string mid = 1; - // 状态 - string status = 2; -} - -message Reply { - // 消息回复 - string mid = 1; - // 内容 - string content = 2; -} - -message Preview { - // 消息预览 - string content = 1; -} - -message Recall { - // 消息撤回 - string mid = 1; -} - -message Transfer { - // 接收者的topic - string topic = 1; - // 类型:转接会话给 技能组 or 个人 - string type = 2; - // 附言 - string content = 3; - // 转接结果:接受 or 拒绝 - bool accept = 4; -} - -message Invite { - // 接收者的topic - string topic = 1; - // 类型:邀请会话给 技能组 or 个人 - string type = 2; - // 附言 - string content = 3; - // 转接结果:接受 or 拒绝 - bool accept = 4; -} - -message Notice { - // topic - string topic = 1; - // 会议提醒等 - string type = 2; - // 内容 - string content = 3; -} - -message Extra { - // 开发者自定义字段内容 - string content = 1; -} - // 消息三要素:1. 谁发送的消息? 2. 发送给谁的消息? 3. 发送的消息内容是什么? message Message { - // 唯一ID/localId=mid + // 唯一mid string mid = 1; - // 主题:消息接收者为订阅此topic的人 - // string topic = 2; - // 消息发送状态 - string status = 3; - // 时间戳 - // string createdAt = 4; - string timestamp = 4; - // mqtt clientId - // string clientId = 5; - // 消息来源客户端ew - string client = 6; - // 消息版本 - string version = 7; // 消息类型 - string type = 8; - // 1. 谁发送的消息? 消息发送者 - User user = 9; - // 3. 发送的消息内容是什么?消息体内容,至少设置其中一种 - oneof body { - Text text = 10; - Image image = 11; - File file = 12; - Voice voice = 13; - Video video = 14; - Location location = 15; - Link link = 16; - Receipt receipt = 17; - Reply reply = 18; - Preview preview = 19; - Recall recall = 20; - Transfer transfer = 21; - Invite invite = 22; - Notice notice = 23; - // - Extra extra = 30; - } - // 2. 发送给谁的消息?客服会话/一对一会话/群组会话 - Thread thread = 31; - // 是否加密,true为加密,false为明文 - bool encrypted = 32; -} - -// 列表 -message MessageList { - // - repeated Message list = 1; + string type = 2; + // 消息内容,可能是文本,图片,语音,视频,文件等,json + string content = 3; + // 消息发送状态 + string status = 4; + // 时间戳 + string createdAt = 5; + // 消息来源客户端 + string client = 6; + // 会话 + Thread thread = 7; + // 发送者 + User user = 8; } // [END messages] diff --git a/modules/socket/src/main/proto/thread.proto b/modules/socket/src/main/proto/thread.proto index 1a51857cf5..4fba36e7dc 100644 --- a/modules/socket/src/main/proto/thread.proto +++ b/modules/socket/src/main/proto/thread.proto @@ -24,29 +24,9 @@ option java_outer_classname = "ThreadProto"; message Thread { // ID string tid = 1; + // 订阅主题 + string topic = 2; // 会话类型 - string type = 2; - // 来源客户端 - string client = 5; - // - string nickname = 6; - // - string avatar = 7; - // - string content = 8; - // - string timestamp = 9; - // - int32 unreadCount = 10; - // 主题:消息接收者为订阅此topic的人 - string topic = 11; - // 扩展字段 - string extra = 20; -} - -// 列表 -message ThreadList { - - repeated Thread list = 1; + string type = 3; } // diff --git a/modules/socket/src/main/proto/user.proto b/modules/socket/src/main/proto/user.proto index e2cc830578..ace90d0305 100644 --- a/modules/socket/src/main/proto/user.proto +++ b/modules/socket/src/main/proto/user.proto @@ -22,21 +22,8 @@ option java_outer_classname = "UserProto"; message User { //uuID string uid = 1; - // 用户名 - string username = 2; // 昵称 - string nickname = 3; + string nickname = 2; // 头像 - string avatar = 4; - // 来源客户端 - string client = 5; - // 企业号 - string subDomain = 6; - // 扩展字段 - string extra = 7; -} -// 列表 -message UserList { - - repeated User list = 1; + string avatar = 3; } diff --git a/modules/team/.DS_Store b/modules/team/.DS_Store index e7ff9b5738..42a51073ca 100644 Binary files a/modules/team/.DS_Store and b/modules/team/.DS_Store differ diff --git a/modules/team/pom.xml b/modules/team/pom.xml index b41962da4d..c2ecfb5812 100644 --- a/modules/team/pom.xml +++ b/modules/team/pom.xml @@ -11,12 +11,11 @@ 0.0.1-SNAPSHOT - weiyu-team + im-team team Demo project for Spring Boot - com.bytedesk - weiyu-core + im-core ${im.version} provided diff --git a/modules/team/src/main/java/com/bytedesk/team/department/Department.java b/modules/team/src/main/java/com/bytedesk/team/department/Department.java index 5b055953e2..a4c92bf666 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/Department.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/Department.java @@ -1,13 +1,24 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:20:17 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-24 18:05:45 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.team.department; -import java.util.List; +import java.util.Set; +import java.util.HashSet; -import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.AuditModel; -import com.bytedesk.team.member.Member; -import com.bytedesk.team.organization.Organization; import com.fasterxml.jackson.annotation.JsonIgnore; - import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -23,7 +34,7 @@ import lombok.experimental.Accessors; @Data @Builder @Accessors(chain = true) -@EqualsAndHashCode(callSuper = false) +@EqualsAndHashCode(callSuper = false, exclude = {"children", "parent"}) @AllArgsConstructor @NoArgsConstructor @Table(name = "team_department") @@ -31,71 +42,68 @@ public class Department extends AuditModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id") private Long id; - /** - * - */ - @Column(name = "did", unique = true, nullable = false) + @Column(unique = true, nullable = false) private String did; - /** - * - */ private String name; - /** - * - */ private String description; - /** - * - */ @Column(name = "by_type") private String type; - /** - * - */ - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + // 关联上级部门 + @JsonIgnore + @ManyToOne @JoinColumn(name = "parent_id") private Department parent; - /** - * - */ - @OneToMany(mappedBy = "department", fetch = FetchType.EAGER, cascade = CascadeType.ALL) - private List members; + // 关联下级部门(集合形式) + @Builder.Default + @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private Set children = new HashSet<>(); - /** - * - */ - // @Column(name = "organization_oid") - // private String organizationOid; - @JsonIgnore - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "organization_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) - private Organization organization; + // 使用Set防止重复加入 + // @JsonIgnore + // @Builder.Default + // @ManyToMany(mappedBy = "departments", fetch = FetchType.LAZY) + // private Set members = new HashSet<>(); - /** - * created by - */ - @JsonIgnore - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) - private User user; + // private String orgOid; + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // // @JoinColumn(name = "organization_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) + // @JsonBackReference("organization-departments") + // private Organization organization; + private String orgOid; - /** - * 下级部门 - */ - @JsonIgnore - @Transient - private List children; + /** created by */ + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // // @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) + // private User user; - public String toString() { - return this.name; + public void addChild(Department child) { + children.add(child); + child.setParent(this); } + public void removeChild(Department child) { + children.remove(child); + child.setParent(null); + } + + // public void addMember(Member member) { + // members.add(member); + // member.addDepartment(this); + // } + + // public void removeMember(Member member) { + // members.remove(member); + // member.removeDepartment(this); + // } + + } diff --git a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentController.java b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentController.java index 286f919577..8f1c8b9c17 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentController.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentController.java @@ -1,22 +1,6 @@ -/* - * bytedesk.com https://github.com/Bytedesk/bytedesk - * - * Copyright (C) 2013-2024 bytedesk.com - * - * License restrictions - * - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * - * contact: 270580156@qq.com - * 联系:270580156@qq.com - */ package com.bytedesk.team.department; +import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -26,8 +10,8 @@ import org.springframework.web.bind.annotation.RestController; import com.bytedesk.core.utils.BaseRequest; import com.bytedesk.core.utils.JsonResult; -// import com.bytedesk.core.utils.PageParam; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; /** @@ -37,43 +21,23 @@ import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/dep") +@Tag(name = "department - 部门", description = "department apis") public class DepartmentController { private final DepartmentService departmentService; - // /** - // * 列表 - // * - // * @return json - // */ - // @GetMapping("/list") - // public ResponseEntity> list() { - - // return () -> { - - // // 分页查询 - // // List departmentDTOList = departmentService.findByUser(); - // // - // return new JsonResult<>("获取成功", 200, false); - // }; - // } - - // /** - // * 查询 - // * - // * @return json - // */ - // @GetMapping("/query") - // public ResponseEntity> query(PageParam pageParam) { - - // return () -> { - - // // 分页查询 - // // Page departmentDTOPage = departmentService.findByUser(pageParam); - // // - // return new JsonResult<>("获取成功", 200, false); - // }; - // } + /** + * query org departments + * + * @return json + */ + @GetMapping("/query") + public ResponseEntity query(DepartmentRequest departmentRequest) { + // + Page departmentPage = departmentService.query(departmentRequest); + // + return ResponseEntity.ok(JsonResult.success(departmentPage)); + } /** * 创建 @@ -128,7 +92,6 @@ public class DepartmentController { @GetMapping("/filter") public ResponseEntity> filter(BaseRequest filterParam) { - // // Page departmentDTOPage = // departmentService.findByNameContainingOrValueContainingAndUser(filterParam); // diff --git a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRepository.java b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRepository.java index f7b4096fcc..4c764b1b93 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRepository.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 14:18:16 + * @LastEditTime: 2024-04-24 17:19:33 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,8 @@ package com.bytedesk.team.department; import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; // import org.springframework.cache.annotation.CacheEvict; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -45,4 +47,6 @@ public interface DepartmentRepository extends JpaRepository, J Optional findByDid(String did); + Page findByOrgOidAndParent(String orgOid, Department parent, Pageable pageable); + } diff --git a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRequest.java b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRequest.java index a43128c728..0579c39e64 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRequest.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-01 17:03:50 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 16:12:30 + * @LastEditTime: 2024-04-24 17:11:41 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -17,41 +17,31 @@ package com.bytedesk.team.department; import com.bytedesk.core.utils.BaseRequest; import jakarta.validation.constraints.NotEmpty; -// import lombok.Builder; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @Data -// @Builder +@Builder @Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor @EqualsAndHashCode(callSuper = false) public class DepartmentRequest extends BaseRequest { private String did; - /** - * - */ @NotEmpty - private String nickname; + private String name; - /** - * - */ - private String avatar; - - /** - * - */ private String description; - - /** - * - */ - private String parent_did; + // + private String parentDid; @NotEmpty - private String org_oid; + private String orgOid; } diff --git a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentResponse.java b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentResponse.java index b35f17ff37..6c6a00c355 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentResponse.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-25 15:36:41 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:11:10 + * @LastEditTime: 2024-04-24 17:26:49 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,8 +14,36 @@ */ package com.bytedesk.team.department; +import java.util.HashSet; +import java.util.Set; + import com.bytedesk.core.utils.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) public class DepartmentResponse extends BaseResponse { + + private static final long serialVersionUID = 5377636189L; + private String did; + + private String name; + + private String description; + + private String type; + + @Builder.Default + private Set children = new HashSet<>(); } diff --git a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentService.java b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentService.java index 1ec73d5015..690b4b3fc6 100644 --- a/modules/team/src/main/java/com/bytedesk/team/department/DepartmentService.java +++ b/modules/team/src/main/java/com/bytedesk/team/department/DepartmentService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 14:18:25 + * @LastEditTime: 2024-04-25 09:35:29 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,17 +16,19 @@ package com.bytedesk.team.department; import java.util.Arrays; import java.util.Optional; -import java.util.UUID; import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import com.bytedesk.core.auth.AuthService; import com.bytedesk.core.config.BytedeskProperties; import com.bytedesk.core.constant.TypeConsts; -import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.utils.Utils; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.team.organization.Organization; import com.bytedesk.team.organization.OrganizationService; @@ -38,10 +40,6 @@ import lombok.extern.slf4j.Slf4j; @AllArgsConstructor public class DepartmentService { - private final AuthService authService; - - private final UserService userService; - private final ModelMapper modelMapper; private final BytedeskProperties properties; @@ -50,35 +48,57 @@ public class DepartmentService { private final DepartmentRepository departmentRepository; + private final UidUtils uidUtils; + + public Page query(DepartmentRequest departmentRequest) { + + Pageable pageable = PageRequest.of(departmentRequest.getPageNumber(), departmentRequest.getPageSize(), Sort.Direction.ASC, + "id"); + + Page page = departmentRepository.findByOrgOidAndParent(departmentRequest.getOrgOid(), null, pageable); + + return page.map(this::convertToDepartmentResponse); + } + public Department create(DepartmentRequest departmentRequest) { Department department = modelMapper.map(departmentRequest, Department.class); - department.setDid(Utils.getUid()); + department.setDid(uidUtils.getCacheSerialUid()); - if (StringUtils.hasLength(departmentRequest.getParent_did())) { - log.debug("TODO: parent_did {}", departmentRequest.getParent_did()); + if (StringUtils.hasLength(departmentRequest.getParentDid())) { + log.debug("parent_did {}", departmentRequest.getParentDid()); + Optional parentOptional = departmentRepository.findByDid(departmentRequest.getParentDid()); + if (parentOptional.isPresent()) { + parentOptional.get().addChild(department); + } } else { log.debug("parent_did is null"); department.setParent(null); } - Optional orgOptional = organizationService.findByOid(departmentRequest.getOrg_oid()); - if (orgOptional.isPresent()) { - log.debug("org_oid {} exist", departmentRequest.getOrg_oid()); - department.setOrganization(orgOptional.get()); - } else { - log.debug("org_oid {} not exist", departmentRequest.getOrg_oid()); - return null; - } + department.setOrgOid(departmentRequest.getOrgOid()); - department.setUser(authService.getCurrentUser()); + return save(department); + } - // FIXME: throw error ? - // log.debug("department {}", department.toString()); + @Cacheable(value = "department", key = "#name", unless = "#result == null") + public Optional findByName(String name) { + return departmentRepository.findByName(name); + } + @Cacheable(value = "department", key = "#did", unless = "#result == null") + public Optional findByDid(String did) { + return departmentRepository.findByDid(did); + } + + public Department save(Department department) { return departmentRepository.save(department); } + public DepartmentResponse convertToDepartmentResponse(Department department) { + return modelMapper.map(department, DepartmentResponse.class); + } + public void initData() { if (departmentRepository.count() > 0) { @@ -88,32 +108,33 @@ public class DepartmentService { Optional orgOptional = organizationService.findByName(properties.getCompany()); if (orgOptional.isPresent()) { + String orgOid = orgOptional.get().getOid(); // Department[] departments = new Department[] { Department.builder().name(TypeConsts.DEPT_HR).description(TypeConsts.DEPT_HR) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_ORG).description(TypeConsts.DEPT_ORG) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_IT).description(TypeConsts.DEPT_IT) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_MONEY).description(TypeConsts.DEPT_MONEY) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_MARKETING).description(TypeConsts.DEPT_MARKETING) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_SALES).description(TypeConsts.DEPT_SALES) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build(), + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build(), Department.builder().name(TypeConsts.DEPT_CUSTOMER_SERVICE) .description(TypeConsts.DEPT_CUSTOMER_SERVICE) - .organization(orgOptional.get()).type(TypeConsts.TYPE_SYSTEM).build() + .orgOid(orgOid).type(TypeConsts.TYPE_SYSTEM).build() }; Arrays.stream(departments).forEach((department) -> { Optional depOptional = departmentRepository.findByName(department.getName()); if (!depOptional.isPresent()) { - String uuid = UUID.randomUUID().toString().replaceAll("-", ""); - department.setDid(uuid); + department.setDid(uidUtils.getCacheSerialUid()); + department.setOrgOid(orgOid); // department.setOrganization(orgOptional.get()); - department.setUser(userService.getAdmin().get()); + // department.setUser(userService.getAdmin().get()); departmentRepository.save(department); } }); diff --git a/modules/team/src/main/java/com/bytedesk/team/group/Group.java b/modules/team/src/main/java/com/bytedesk/team/group/Group.java index 1976fe9746..8d719f44a1 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/Group.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/Group.java @@ -2,7 +2,7 @@ * @Author: jack ning github@bytedesk.com * @Date: 2024-01-23 14:53:16 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 12:52:42 + * @LastEditTime: 2024-04-26 20:41:38 * @FilePath: /server/plugins/im/src/main/java/com/bytedesk/im/group/Group.java * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ @@ -33,34 +33,22 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor +// @EntityListeners({ GroupListener.class }) // 注:group为mysql保留关键字, groups在mysql8启动报错,所有表名修改为groupes @Table(name = "team_groupes") public class Group extends AuditModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id") private Long id; - /** - * - */ - @Column(name = "gid", unique = true, nullable = false) + @Column(unique = true, nullable = false) private String gid; - /** - * - */ private String name; - /** - * - */ private String avatar; - /** - * - */ private String description; /** diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupController.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupController.java index eba984f650..d06db7da05 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/GroupController.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RestController; import com.bytedesk.core.utils.JsonResult; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; /** @@ -19,6 +20,7 @@ import lombok.AllArgsConstructor; @RestController @AllArgsConstructor @RequestMapping("/api/v1/group") +@Tag(name = "group - 群组", description = "group apis") public class GroupController { private final GroupService groupService; diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupEventHandler.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupEventHandler.java index 7208797dde..845a512465 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/GroupEventHandler.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupEventHandler.java @@ -25,7 +25,7 @@ import org.springframework.data.rest.core.annotation.HandleBeforeSave; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.stereotype.Component; -import com.bytedesk.core.auth.AuthService; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.User; import lombok.AllArgsConstructor; diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupListener.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupListener.java new file mode 100644 index 0000000000..397e66fc2e --- /dev/null +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupListener.java @@ -0,0 +1,62 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 09:44:58 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-15 09:45:01 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.team.group; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class GroupListener { + + @PrePersist + public void prePersist(Group group) { + log.info("prePersist {}", group.getGid()); + } + + @PostPersist + public void postPersist(Group group) { + log.info("postPersist {}", group.getGid()); + } + + @PreUpdate + public void preUpdate(Group group) { + log.info("preUpdate {}", group.getGid()); + } + + @PostUpdate + public void postUpdate(Group group) { + log.info("postUpdate {}", group.getGid()); + } + + @PreRemove + public void preRemove(Group group) { + log.info("preRemove {}", group.getGid()); + } + + @PostRemove + public void postRemove(Group group) { + log.info("postRemove {}", group.getGid()); + } + + +} diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupRequest.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupRequest.java index b741f30491..89c03d17e7 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/GroupRequest.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 09:55:40 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-25 15:37:51 + * @LastEditTime: 2024-04-08 16:02:15 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -27,4 +27,5 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = false) public class GroupRequest extends BaseRequest { + private String gid; } diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupResponse.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupResponse.java index a8cd3de145..b69c36ee5b 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/GroupResponse.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 09:55:51 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:10:59 + * @LastEditTime: 2024-04-08 16:02:21 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,20 @@ package com.bytedesk.team.group; import com.bytedesk.core.utils.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) public class GroupResponse extends BaseResponse { + private String gid; } diff --git a/modules/team/src/main/java/com/bytedesk/team/group/GroupService.java b/modules/team/src/main/java/com/bytedesk/team/group/GroupService.java index be555edf1d..3d7403e548 100644 --- a/modules/team/src/main/java/com/bytedesk/team/group/GroupService.java +++ b/modules/team/src/main/java/com/bytedesk/team/group/GroupService.java @@ -37,7 +37,7 @@ public class GroupService { public Page query(GroupRequest pageParam) { - Pageable pageable = PageRequest.of(pageParam.getPageNumber(), pageParam.getPageSize(), Sort.Direction.DESC, + Pageable pageable = PageRequest.of(pageParam.getPageNumber(), pageParam.getPageSize(), Sort.Direction.ASC, "id"); return groupRepository.findAll(pageable); diff --git a/modules/team/src/main/java/com/bytedesk/team/member/Member.java b/modules/team/src/main/java/com/bytedesk/team/member/Member.java index e02058408e..eced60a9d1 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/Member.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/Member.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:36:48 + * @LastEditTime: 2024-04-24 20:41:49 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,10 +14,12 @@ */ package com.bytedesk.team.member; +import java.util.HashSet; +import java.util.Set; + import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.AuditModel; import com.bytedesk.team.department.Department; -import com.bytedesk.team.organization.Organization; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; @@ -33,9 +35,10 @@ import lombok.experimental.Accessors; @Data @Builder @Accessors(chain = true) -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = true, exclude = { "departments" }) @AllArgsConstructor @NoArgsConstructor +@EntityListeners({ MemberListener.class }) @Table(name = "team_member") public class Member extends AuditModel { @@ -43,11 +46,8 @@ public class Member extends AuditModel { @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - /** - * uuid - */ - @Column(name = "mid", unique = true, nullable = false) - private String mid; + @Column(name = "uuid", unique = true, nullable = false) + private String uid; /** * job number @@ -77,28 +77,42 @@ public class Member extends AuditModel { * work email */ @Email(message = "email format error") - @Column(name = "email", unique = true) + @Column(unique = true) private String email; /** * department */ @JsonIgnore - @ManyToOne(fetch = FetchType.LAZY) - private Department department; + // 关联多个Department + @Builder.Default + @ManyToMany(fetch = FetchType.LAZY) + private Set departments = new HashSet<>(); /** * login user info */ // @JsonIgnore - @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + @OneToOne(fetch = FetchType.EAGER) private User user; /** * belong to org */ - @JsonIgnore - @ManyToOne(fetch = FetchType.LAZY) - private Organization organization; + // @JsonIgnore + // @ManyToOne(fetch = FetchType.LAZY) + // private Organization organization; + private String orgOid; + + // 添加、移除部门的方法 + public void addDepartment(Department department) { + departments.add(department); + // department.getMembers().add(this); // 假设Department类中有getMembers()方法返回成员列表 + } + + public void removeDepartment(Department department) { + departments.remove(department); + // department.getMembers().remove(this); // 假设Department类中有getMembers()方法返回成员列表 + } } diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberController.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberController.java index f89ec9e526..6308929c0e 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberController.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:02:12 + * @LastEditTime: 2024-04-26 15:21:47 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,6 +14,7 @@ */ package com.bytedesk.team.member; +import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -23,9 +24,7 @@ import org.springframework.web.bind.annotation.RestController; import com.bytedesk.core.utils.BaseRequest; import com.bytedesk.core.utils.JsonResult; -// import com.bytedesk.core.utils.PageParam; -// import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -37,11 +36,37 @@ import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/mem") -@Tag(name = "member - 成员", description = "member description") +@Tag(name = "member - 成员", description = "member apis") public class MemberController { private final MemberService memberService; + /** + * + * @param memberRequest + * @return + */ + @GetMapping("/all") + public ResponseEntity queryAll(MemberRequest memberRequest) { + // + Page memberResponse = memberService.queryAll(memberRequest); + // + return ResponseEntity.ok(JsonResult.success(memberResponse)); + } + + /** + * query department users + * + * @return json + */ + @GetMapping("/query") + public ResponseEntity query(MemberRequest memberRequest) { + // + Page memberResponse = memberService.query(memberRequest); + // + return ResponseEntity.ok(JsonResult.success(memberResponse)); + } + /** * create * @@ -77,7 +102,6 @@ public class MemberController { public ResponseEntity delete(@RequestBody MemberRequest memberRequest) { - return ResponseEntity.ok(JsonResult.success()); } diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberEventHandler.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberEventHandler.java index e529aa08fe..bd7e007494 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberEventHandler.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberEventHandler.java @@ -32,6 +32,8 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; /** + * 仅能够监听spring-data-rest接口 + * * https://spring.io/guides/tutorials/react-and-spring-data-rest/ * https://docs.spring.io/spring-data/rest/reference/events.html */ diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberListener.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberListener.java new file mode 100644 index 0000000000..d1c9344aa8 --- /dev/null +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberListener.java @@ -0,0 +1,57 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-15 09:46:18 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-24 15:15:06 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.team.member; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostUpdate; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class MemberListener { + + // @PrePersist + // public void prePersist(Member member) { + // log.info("prePersist {}", member.getUid()); + // } + + @PostPersist + public void postPersist(Member member) { + log.info("postPersist {}", member.getUid()); + } + + // @PreUpdate + // public void preUpdate(Member member) { + // log.info("preUpdate {}", member.getUid()); + // } + + @PostUpdate + public void postUpdate(Member member) { + log.info("postUpdate {}", member.getUid()); + } + + // @PreRemove + // public void preRemove(Member member) { + // log.info("preRemove {}", member.getUid()); + // } + + // @PostRemove + // public void postRemove(Member member) { + // log.info("postRemove {}", member.getUid()); + // } + +} diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberRepository.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberRepository.java index 26e3356d1e..fdc5f2e945 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberRepository.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-05 12:06:49 + * @LastEditTime: 2024-04-24 15:17:05 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,6 +16,8 @@ package com.bytedesk.team.member; import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; // import org.springframework.data.repository.query.Param; @@ -49,5 +51,14 @@ public interface MemberRepository extends JpaRepository, JpaSpecif // // @PreAuthorize("#member?.user?.username == authentication?.username") // void delete(@Param("member") Member member); + Optional findByUid(String uid); + Optional findByUser_Mobile(String mobile); + + Optional findByUser_Email(String email); + + Page findByDepartmentsDidIn(String dids[], Pageable pageable); + + Page findByOrgOid(String orgOid, Pageable pageable); + } diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberRequest.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberRequest.java index 21d7ee07e7..6a2f845e4e 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberRequest.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-02 13:30:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:56:10 + * @LastEditTime: 2024-04-24 15:15:30 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,7 +16,7 @@ package com.bytedesk.team.member; import com.bytedesk.core.utils.BaseRequest; -import jakarta.validation.constraints.NotEmpty; +// import jakarta.validation.constraints.NotEmpty; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; @@ -31,7 +31,7 @@ public class MemberRequest extends BaseRequest { /** * uuid */ - private String mid; + private String uid; /** * job number @@ -43,7 +43,7 @@ public class MemberRequest extends BaseRequest { * nickname * 姓名 */ - @NotEmpty + // @NotEmpty private String nickname; private String password; @@ -52,7 +52,7 @@ public class MemberRequest extends BaseRequest { * mobile * 手机号 */ - @NotEmpty + // @NotEmpty private String mobile; private String email; @@ -74,14 +74,14 @@ public class MemberRequest extends BaseRequest { * 是否已验证 */ @Builder.Default - private boolean verified = false; + private Boolean verified = false; /** * department did */ - @NotEmpty private String depDid; - + // + private String orgOid; } diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberResponse.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberResponse.java index 60f3cf963d..5799bd82dc 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberResponse.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-03-25 15:36:57 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 15:15:27 + * @LastEditTime: 2024-04-24 15:15:36 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -32,11 +32,11 @@ import lombok.experimental.Accessors; @EqualsAndHashCode(callSuper = true) public class MemberResponse extends BaseResponse { - private String mid; + private String uid; private String jobNo; - private String realname; + private String nickname; private String seatNo; diff --git a/modules/team/src/main/java/com/bytedesk/team/member/MemberService.java b/modules/team/src/main/java/com/bytedesk/team/member/MemberService.java index 751e3caae6..2b780c7706 100644 --- a/modules/team/src/main/java/com/bytedesk/team/member/MemberService.java +++ b/modules/team/src/main/java/com/bytedesk/team/member/MemberService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-04-02 16:50:05 + * @LastEditTime: 2024-04-26 18:21:20 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -15,20 +15,27 @@ package com.bytedesk.team.member; import java.util.Optional; - import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.cache.annotation.CachePut; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.constant.TypeConsts; // import com.bytedesk.core.auth.AuthService; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.utils.BdConvertUtils; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.JsonResult; -import com.bytedesk.core.utils.Utils; import com.bytedesk.team.department.Department; -import com.bytedesk.team.department.DepartmentRepository; +import com.bytedesk.team.department.DepartmentService; + import lombok.AllArgsConstructor; // @Slf4j @@ -38,12 +45,36 @@ public class MemberService { private UserService userService; - private DepartmentRepository departmentRepository; + private DepartmentService departmentService; private MemberRepository memberRepository; private ModelMapper modelMapper; + private UidUtils uidUtils; + + public Page queryAll(MemberRequest memberRequest) { + + Pageable pageable = PageRequest.of(memberRequest.getPageNumber(), memberRequest.getPageSize(), Sort.Direction.ASC, + "id"); + + Page memberPage = memberRepository.findByOrgOid(memberRequest.getOrgOid(), pageable); + + return memberPage.map(this::convertToMemberResponse); + } + + public Page query(MemberRequest memberRequest) { + + Pageable pageable = PageRequest.of(memberRequest.getPageNumber(), memberRequest.getPageSize(), Sort.Direction.ASC, + "id"); + + Page memberPage = memberRepository.findByDepartmentsDidIn(new String[]{memberRequest.getDepDid()}, pageable); + + return memberPage.map(this::convertToMemberResponse); + } + + + @Transactional public JsonResult create(MemberRequest memberRequest) { // // if (userService.existsByMobile(memberRequest.getMobile())) { @@ -53,21 +84,24 @@ public class MemberService { // return JsonResult.error("email already exist"); // } // - Optional memberOptional = memberRepository.findByUser_Mobile(memberRequest.getMobile()); + Optional memberOptional = findByEmail(memberRequest.getEmail()); if (!memberOptional.isPresent()) { Member member = modelMapper.map(memberRequest, Member.class); - member.setMid(Utils.getUid()); + member.setUid(uidUtils.getCacheSerialUid()); // - Optional depOptional = departmentRepository.findByDid(memberRequest.getDepDid()); + Optional depOptional = departmentService.findByDid(memberRequest.getDepDid()); if (depOptional.isPresent()) { - member.setDepartment(depOptional.get()); - member.setOrganization(depOptional.get().getOrganization()); + member.addDepartment(depOptional.get()); + // member.setDepartment(depOptional.get()); + // member.setOrganization(depOptional.get().getOrganization()); + // member.setOrgOid(depOptional.get().getOrganization().getOid()); + member.setOrgOid(depOptional.get().getOrgOid()); } else { - return null; + return JsonResult.error("department not exist"); } // User user; - Optional userOptional = userService.findByMobile(memberRequest.getMobile()); + Optional userOptional = userService.findByEmail(memberRequest.getEmail()); if (!userOptional.isPresent()) { user = userService.createUser( memberRequest.getNickname(), @@ -75,23 +109,18 @@ public class MemberService { memberRequest.getPassword(), memberRequest.getMobile(), memberRequest.getEmail(), - memberRequest.isVerified(), - member.getOrganization().getOid() - ); + memberRequest.getVerified(), + depOptional.get().getOrgOid() + ); } else { - user = userOptional.get(); - // user = userService.updateUser( - // userOptional.get(), - // memberRequest.getPassword(), - // memberRequest.getMobile(), - // memberRequest.getEmail() - // ); + // just return user + user = userOptional.get(); } member.setUser(user); // - MemberResponse result = save(member); + Member result = save(member); - return JsonResult.success(result); + return JsonResult.success(convertToMemberResponse(result)); } // return update(memberOptional.get(), memberRequest); @@ -105,234 +134,98 @@ public class MemberService { member.setTelephone(memberRequest.getTelephone()); member.setEmail(memberRequest.getEmail()); // - Optional depOptional = departmentRepository.findByDid(memberRequest.getDepDid()); + Optional depOptional = departmentService.findByDid(memberRequest.getDepDid()); if (depOptional.isPresent()) { - member.setDepartment(depOptional.get()); - member.setOrganization(depOptional.get().getOrganization()); + member.addDepartment(depOptional.get()); + // member.setDepartment(depOptional.get()); + // member.setOrganization(depOptional.get().getOrganization()); + // member.setOrgOid(depOptional.get().getOrganization().getOid()); + member.setOrgOid(depOptional.get().getOrgOid()); } else { return null; } - MemberResponse result = save(member); + Member result = save(member); - return JsonResult.success(result); + return JsonResult.success(convertToMemberResponse(result)); } // public void delete(MemberRequest memberRequest) { - // Optional memberOptional = - // memberRepository.findById(memberRequest.getId()); - // if (memberOptional.isPresent()) { - // memberRepository.delete(memberOptional.get()); + // Optional memberOptional = + // memberRepository.findById(memberRequest.getId()); + // if (memberOptional.isPresent()) { + // memberRepository.delete(memberOptional.get()); // } // } - @SuppressWarnings("null") - private MemberResponse save(Member member) { - return convertToMemberResponse(memberRepository.save(member)); + @Cacheable(value = "member", key = "#uid", unless="#result == null") + public Optional findByUid(String uid) { + return memberRepository.findByUid(uid); + } + + @Cacheable(value = "member", key = "#mobile", unless="#result == null") + public Optional findByMobile(String mobile) { + return memberRepository.findByUser_Mobile(mobile); + } + + @Cacheable(value = "member", key = "#email", unless="#result == null") + public Optional findByEmail(String email) { + return memberRepository.findByUser_Email(email); + } + + @Caching(put = { + @CachePut(value = "member", key = "#member.uid"), + @CachePut(value = "member", key = "#member.user.mobile"), + @CachePut(value = "member", key = "#member.user.email") + }) + private Member save(Member member) { + return memberRepository.save(member); } private MemberResponse convertToMemberResponse(Member member) { MemberResponse memberResponse = modelMapper.map(member, MemberResponse.class); - memberResponse.setUser(BdConvertUtils.convertTUserResponseSimple(member.getUser())); + // memberResponse.setUser(BdConvertUtils.convertTUserResponseSimple(member.getUser())); return memberResponse; } - public void initData() { + // + private static final String[] departments = { + TypeConsts.DEPT_HR, + TypeConsts.DEPT_ORG, + TypeConsts.DEPT_IT, + TypeConsts.DEPT_MONEY, + TypeConsts.DEPT_MARKETING, + TypeConsts.DEPT_SALES, + TypeConsts.DEPT_CUSTOMER_SERVICE + }; + public void initData() { if (memberRepository.count() > 0) { return; } - - // 初始化-人力部门 - Optional departHROptional = departmentRepository.findByName(TypeConsts.DEPT_HR); - if (departHROptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("001") - .password("123456") - .nickname("User001") - .seatNo("001") - .telephone("881") - .mobile("18888888881") - .email("001@email.com") - .verified(true) - .depDid(departHROptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("002") - .password("123456") - .nickname("User002") - .seatNo("002") - .telephone("882") - .mobile("18888888882") - .email("002@email.com") - .verified(true) - .depDid(departHROptional.get().getDid()) - .build(); - create(memberRequest2); + // + for (int i = 1; i <= departments.length; i++) { + String department = departments[i-1]; + Optional depOptional = departmentService.findByName(department); + if (depOptional.isPresent()) { + String userNo = String.format("%03d", i); + MemberRequest memberRequest = MemberRequest.builder() + .jobNo(userNo) + .password("123456") + .nickname("User" + userNo) + .seatNo(userNo) + .telephone(userNo) + .mobile("18888888" + userNo) + .email(userNo + "@email.com") + .verified(true) + .depDid(depOptional.get().getDid()) + .build(); + create(memberRequest); + } } - // 行政 - Optional departORGOptional = departmentRepository.findByName(TypeConsts.DEPT_ORG); - if (departORGOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("003") - .password("123456") - .nickname("User003") - .seatNo("003") - .telephone("883") - .mobile("18888888883") - .email("003@email.com").verified(true) - .depDid(departORGOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("004") - .password("123456") - .nickname("User004") - .seatNo("004") - .telephone("884") - .mobile("18888888884") - .email("004@email.com").verified(true) - .depDid(departORGOptional.get().getDid()) - .build(); - create(memberRequest2); - } - - // IT - Optional departITOptional = departmentRepository.findByName(TypeConsts.DEPT_IT); - if (departITOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("005") - .password("123456") - .nickname("User005") - .seatNo("005") - .telephone("885") - .mobile("18888888885") - .email("005@email.com").verified(true) - .depDid(departITOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("006") - .password("123456") - .nickname("User006") - .seatNo("006") - .telephone("886") - .mobile("18888888886") - .email("006@email.com").verified(true) - .depDid(departITOptional.get().getDid()) - .build(); - create(memberRequest2); - } - - // 财务 - Optional departMoneyOptional = departmentRepository.findByName(TypeConsts.DEPT_MONEY); - if (departMoneyOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("007") - .password("123456") - .nickname("User007") - .seatNo("007") - .telephone("887") - .mobile("18888888887") - .email("007@email.com").verified(true) - .depDid(departMoneyOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("008") - .password("123456") - .nickname("User008") - .seatNo("008") - .telephone("888") - .mobile("18888888888") - .email("008@email.com").verified(true) - .depDid(departMoneyOptional.get().getDid()) - .build(); - create(memberRequest2); - } - - // 营销 - Optional departMarketingOptional = departmentRepository.findByName(TypeConsts.DEPT_MARKETING); - if (departMarketingOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("009") - .password("123456") - .nickname("User009") - .seatNo("009") - .telephone("889") - .mobile("18888888889") - .email("009@email.com").verified(true) - .depDid(departMarketingOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("010") - .password("123456") - .nickname("User010") - .seatNo("010") - .telephone("810") - .mobile("18888888810") - .email("010@email.com").verified(true) - .depDid(departMarketingOptional.get().getDid()) - .build(); - create(memberRequest2); - } - - // 销售 - Optional departSalesOptional = departmentRepository.findByName(TypeConsts.DEPT_SALES); - if (departSalesOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("011") - .password("123456") - .nickname("User011") - .seatNo("011") - .telephone("811") - .mobile("18888888811") - .email("011@email.com").verified(true) - .depDid(departSalesOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("012") - .password("123456") - .nickname("User012") - .seatNo("012") - .telephone("812") - .mobile("18888888812") - .email("012@email.com").verified(true) - .depDid(departSalesOptional.get().getDid()) - .build(); - create(memberRequest2); - } - - // 客服 - Optional departCSOptional = departmentRepository.findByName(TypeConsts.DEPT_CUSTOMER_SERVICE); - if (departCSOptional.isPresent()) { - MemberRequest memberRequest = MemberRequest.builder() - .jobNo("013") - .password("123456") - .nickname("User013") - .seatNo("013") - .telephone("813") - .mobile("18888888813") - .email("013@email.com").verified(true) - .depDid(departCSOptional.get().getDid()) - .build(); - create(memberRequest); - MemberRequest memberRequest2 = MemberRequest.builder() - .jobNo("014") - .password("123456") - .nickname("User014") - .seatNo("014") - .telephone("814") - .mobile("18888888814") - .email("014@email.com").verified(true) - .depDid(departCSOptional.get().getDid()) - .build(); - create(memberRequest2); - } - } + + + } diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/Organization.java b/modules/team/src/main/java/com/bytedesk/team/organization/Organization.java index 062c5ae6e7..7f6479f95b 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/Organization.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/Organization.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 12:54:09 + * @LastEditTime: 2024-04-23 17:19:26 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,11 +14,9 @@ */ package com.bytedesk.team.organization; -import java.util.List; - +import com.bytedesk.core.constant.AvatarConsts; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.utils.AuditModel; -import com.bytedesk.team.department.Department; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; @@ -41,34 +39,30 @@ public class Organization extends AuditModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id") private Long id; - /** - * - */ - @Column(name = "oid", unique = true, nullable = false) + // 随机字符串,可读性差 + @Column(unique = true, nullable = false) private String oid; - /** - * - */ + /** name should be unique */ + @Column(unique = true, nullable = false) private String name; - /** - * - */ + // logo + @Builder.Default + private String logo = AvatarConsts.DEFAULT_AVATAR_URL; + + // organiztion code, 可读性强,供用户搜索 + @Column(unique = true) + private String code; + private String description; - /** - * - */ - @OneToMany(mappedBy = "organization", fetch = FetchType.EAGER, cascade = CascadeType.ALL) - private List departments; + // @Builder.Default + // @OneToMany(fetch = FetchType.LAZY) + // private Set departments = new HashSet<>(); - /** - * - */ @JsonIgnore @ManyToOne(fetch = FetchType.LAZY) private User user; diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationController.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationController.java index 1a8c44dc95..0146d64645 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationController.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationController.java @@ -1,3 +1,17 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:20:17 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-26 15:21:52 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.team.organization; import java.util.Optional; @@ -12,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController; import com.bytedesk.core.utils.JsonResult; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; // import lombok.extern.slf4j.Slf4j; @@ -24,6 +39,7 @@ import lombok.AllArgsConstructor; // @RepositoryRestController("/v1/org") @RestController @RequestMapping("/api/v1/org") +@Tag(name = "organization - 组织", description = "organization apis") public class OrganizationController { private final OrganizationService organizationService; @@ -34,11 +50,11 @@ public class OrganizationController { * @return json */ @GetMapping("/query") - public ResponseEntity> query(OrganizationRequest pageParam) { + public ResponseEntity query(OrganizationRequest pageParam) { // - Page orgPage = organizationService.queryMyOrgs(pageParam); + Page orgPage = organizationService.query(pageParam); // // - return ResponseEntity.ok().body(new JsonResult<>("get my orgs success", 200, orgPage)); + return ResponseEntity.ok(JsonResult.success(orgPage)); } /** @@ -46,80 +62,23 @@ public class OrganizationController { * @param request * @return */ - @GetMapping("/query/oid") - public ResponseEntity> queryByOid(OrganizationRequest request) { + @GetMapping("/oid") + public ResponseEntity queryByOid(OrganizationRequest request) { // - Optional organizations = organizationService.findByOid(request.getOid()); + Optional org = organizationService.findByOid(request.getOid()); + if (!org.isPresent()) { + return ResponseEntity.ok(JsonResult.error("组织不存在")); + } // - return ResponseEntity.ok().body(new JsonResult<>("get or by oid success", 200, organizations)); + return ResponseEntity.ok(JsonResult.success(organizationService.convertToOrganizationResponse(org.get()))); } - // /** - // * 创建 - // * - // * @param roleParam role - // * @return json - // */ - // @PostMapping("/create") - // public Callable> create(@RequestBody RoleParam roleParam) { - - // return () -> { - - // // RoleDTO roleDTO = roleService.create(roleParam); - - // return new JsonResult<>("创建成功", 200, false); - // }; - // } - - // /** - // * 更新 - // * - // * @param roleParam role - // * @return json - // */ - // @PostMapping("/update") - // public Callable> update(@RequestBody RoleParam roleParam) { - - // return () -> { - - // // RoleDTO roleDTO = roleService.update(roleParam); - // // - // return new JsonResult<>("更新成功", 200, false); - // }; - // } - - // /** - // * 删除 - // * - // * @param roleParam role - // * @return json - // */ - // @PostMapping("/delete") - // public Callable> delete(@RequestBody RoleParam roleParam) { - - // return () -> { - // // - // // roleService.deleteById(roleParam.getId()); - - // return new JsonResult<>("删除成功", 200, roleParam.getId()); - // }; - // } - - // /** - // * 搜索 - // * - // * @return json - // */ - // @GetMapping("/filter") - // public Callable> filter(FilterParam filterParam) { - - // return () -> { - // // - // // Page roleDTOPage = - // // roleService.findByNameContainingOrValueContainingAndUser(filterParam); - // // - // return new JsonResult<>("搜索成功", 200, false); - // }; - // } + /** user join organization by oid */ + @GetMapping("/join") + public ResponseEntity join(OrganizationRequest request) { + // TODO: check if user is already in the organization + // + return ResponseEntity.ok(JsonResult.success()); + } } diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationEventHandler.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationEventHandler.java index 368cfcb977..e37950d289 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationEventHandler.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationEventHandler.java @@ -25,7 +25,7 @@ import org.springframework.data.rest.core.annotation.HandleBeforeSave; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.stereotype.Component; -import com.bytedesk.core.auth.AuthService; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserService; import com.bytedesk.core.utils.Utils; diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRepository.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRepository.java index da60889b2f..4b2d769b85 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRepository.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRepository.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:20:17 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-06 13:55:45 + * @LastEditTime: 2024-04-18 16:28:44 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,12 +21,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.rest.core.annotation.RepositoryRestResource; +// import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.lang.NonNull; // import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Repository; -import org.springframework.web.bind.annotation.CrossOrigin; +// import org.springframework.web.bind.annotation.CrossOrigin; import com.bytedesk.core.rbac.user.User; @@ -36,14 +36,14 @@ import io.swagger.v3.oas.annotations.tags.Tag; * // @RepositoryRestResource(exported = false) // 隐藏接口 */ // https://docs.spring.io/spring-data/rest/reference/customizing/configuring-cors.html -@CrossOrigin +// @CrossOrigin @Repository @Tag(name = "organization - 公司/组织") // https://docs.spring.io/spring-data/rest/reference/security.html // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_SUPER')") // https://docs.spring.io/spring-data/rest/reference/customizing/configuring-the-rest-url-path.html // use excerptProjection to self define response format: -@RepositoryRestResource(path = "org", excerptProjection = OrganizationResponse.class) +// @RepositoryRestResource(path = "org", excerptProjection = OrganizationResponse.class) public interface OrganizationRepository extends JpaRepository, JpaSpecificationExecutor { @@ -59,7 +59,6 @@ public interface OrganizationRepository // List findAll(); // - // @Cacheable(cacheNames = "userOrgs", key = "#user.username") Page findByUser(User user, Pageable pageable); @RestResource(exported = false) diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRequest.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRequest.java index 1d6ad1363f..d9e217dc25 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRequest.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationRequest.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-06 16:02:35 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-06 16:02:38 + * @LastEditTime: 2024-04-23 14:13:05 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -25,14 +25,11 @@ public class OrganizationRequest extends BaseRequest { private String oid; - /** - * - */ private String name; - /** - * - */ - private String description; + private String logo; + private String code; + + private String description; } diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationResponse.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationResponse.java index fa4579bb18..ad90faacc3 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationResponse.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationResponse.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-01 21:20:57 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-01 22:17:17 + * @LastEditTime: 2024-04-23 17:27:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,29 +14,28 @@ */ package com.bytedesk.team.organization; -import java.util.Set; +import com.bytedesk.core.utils.BaseResponse; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.rest.core.config.Projection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; -import com.bytedesk.team.department.Department; +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class OrganizationResponse extends BaseResponse { -@Projection(name = "organizationResponse", types = Organization.class) -public interface OrganizationResponse { + private static final long serialVersionUID = 6917647433L; + + private String oid; - @Value("#{target.id}") - long getId(); - - String getOid(); - - String getNickname(); - - Set getDepartments(); - - @Value("#{target.getDepartments().size()}") - int getDepCount(); - - // @Value("#{target.address.toString()}") - // String getAddress(); + private String name; + private String description; } diff --git a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationService.java b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationService.java index 7d64379235..87e96c0d9e 100644 --- a/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationService.java +++ b/modules/team/src/main/java/com/bytedesk/team/organization/OrganizationService.java @@ -1,40 +1,37 @@ /* * @Author: jackning 270580156@qq.com - * * @Date: 2024-01-29 16:20:17 - * * @LastEditors: jackning 270580156@qq.com - * - * @LastEditTime: 2024-03-29 12:31:26 - * + * @LastEditTime: 2024-04-23 18:18:09 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM - * – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the - * terms and automatically terminates your rights under the license. - * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 - * Business Source License 1.1: - * https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ package com.bytedesk.team.organization; import java.util.Optional; +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; -import com.bytedesk.core.auth.AuthService; import com.bytedesk.core.config.BytedeskProperties; +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.User; import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.uid.UidUtils; import com.bytedesk.core.utils.BaseRequest; -import com.bytedesk.core.utils.Utils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -52,25 +49,44 @@ public class OrganizationService { private final OrganizationRepository organizationRepository; - public Page queryMyOrgs(BaseRequest pageParam) { + private final UidUtils uidUtils; + + private final ModelMapper modelMapper; + + public Page query(BaseRequest pageParam) { User user = authService.getCurrentUser(); Pageable pageable = PageRequest.of(pageParam.getPageNumber(), pageParam.getPageSize(), Sort.Direction.DESC, "id"); - return organizationRepository.findByUser(user, pageable); + Page orgPage = organizationRepository.findByUser(user, pageable); + + return orgPage.map(organization -> convertToOrganizationResponse(organization)); } + @Cacheable(value = "organization", key = "#oid", unless = "#result == null") public Optional findByOid(String oid) { return organizationRepository.findByOid(oid); } + @Cacheable(value = "organization", key = "#name", unless = "#result == null") public Optional findByName(String name) { return organizationRepository.findFirstByName(name); } - @SuppressWarnings("null") + @Caching(put = { + @CachePut(value = "organization", key = "#organization.oid"), + @CachePut(value = "organization", key = "#organization.name") + }) + public Organization save(Organization organization) { + return organizationRepository.save(organization); + } + + public OrganizationResponse convertToOrganizationResponse(Organization organization) { + return modelMapper.map(organization, OrganizationResponse.class); + } + public void initData() { if (organizationRepository.count() > 0) { @@ -82,12 +98,12 @@ public class OrganizationService { if (adminOptional.isPresent()) { // Organization organization = Organization.builder() - .oid(Utils.getUid()) + .oid(uidUtils.getCacheSerialUid()) .name(properties.getCompany()) .description(properties.getCompany() + " Description") .user(adminOptional.get()) .build(); - organizationRepository.save(organization); + save(organization); // adminOptional.get().getOrganizations().add(organization.getOid()); userService.save(adminOptional.get()); diff --git a/modules/team/src/test/java/com/bytedesk/team/DepartmentControllerTests.java b/modules/team/src/test/java/com/bytedesk/team/DepartmentControllerTests.java index 029a543377..8bfe4118ba 100644 --- a/modules/team/src/test/java/com/bytedesk/team/DepartmentControllerTests.java +++ b/modules/team/src/test/java/com/bytedesk/team/DepartmentControllerTests.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-02-02 09:13:26 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-02-02 10:12:12 + * @LastEditTime: 2024-04-24 17:23:25 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -43,14 +43,14 @@ public class DepartmentControllerTests { MockitoAnnotations.openMocks(this); } - @SuppressWarnings("null") + // @SuppressWarnings("null") @Test public void testCreateSuccess() { // Arrange DepartmentRequest departmentRequest = new DepartmentRequest(); - departmentRequest.setNickname("Test Department"); - departmentRequest.setParent_did(null); - departmentRequest.setOrg_oid("f01f5444ecbc437cb5b8de7ca7dd023c"); + departmentRequest.setName("Test Department"); + departmentRequest.setParentDid(null); + departmentRequest.setOrgOid("f01f5444ecbc437cb5b8de7ca7dd023c"); // Act ResponseEntity> response = departmentController.create(departmentRequest); @@ -68,8 +68,8 @@ public class DepartmentControllerTests { // Arrange DepartmentRequest departmentRequest = new DepartmentRequest(); departmentRequest.setDid("testDid"); - departmentRequest.setParent_did(null); - departmentRequest.setOrg_oid("testOrgOid"); + departmentRequest.setParentDid(null); + departmentRequest.setOrgOid("testOrgOid"); // Act ResponseEntity> response = departmentController.create(departmentRequest); diff --git a/modules/team/src/test/java/com/bytedesk/team/DepartmentServiceTests.java b/modules/team/src/test/java/com/bytedesk/team/DepartmentServiceTests.java index 15b24f8a4c..c47bb03deb 100644 --- a/modules/team/src/test/java/com/bytedesk/team/DepartmentServiceTests.java +++ b/modules/team/src/test/java/com/bytedesk/team/DepartmentServiceTests.java @@ -1,101 +1,101 @@ package com.bytedesk.team; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.when; +// import static org.junit.jupiter.api.Assertions.assertEquals; +// import static org.junit.jupiter.api.Assertions.assertNotNull; +// import static org.mockito.Mockito.when; -import java.util.Optional; +// import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.modelmapper.ModelMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.modelmapper.ModelMapper; +// import org.springframework.beans.factory.annotation.Autowired; +// import org.springframework.boot.test.context.SpringBootTest; +// import org.springframework.boot.test.mock.mockito.MockBean; +// import org.springframework.test.context.ActiveProfiles; -import com.bytedesk.core.auth.AuthService; -import com.bytedesk.core.rbac.user.User; -import com.bytedesk.team.department.Department; -import com.bytedesk.team.department.DepartmentRepository; -import com.bytedesk.team.department.DepartmentRequest; -import com.bytedesk.team.department.DepartmentService; -import com.bytedesk.team.organization.Organization; -import com.bytedesk.team.organization.OrganizationService; -import com.bytedesk.core.utils.Utils; +// import com.bytedesk.core.auth.AuthService; +// import com.bytedesk.core.rbac.user.User; +// import com.bytedesk.team.department.Department; +// import com.bytedesk.team.department.DepartmentRepository; +// import com.bytedesk.team.department.DepartmentRequest; +// import com.bytedesk.team.department.DepartmentService; +// import com.bytedesk.team.organization.Organization; +// import com.bytedesk.team.organization.OrganizationService; +// import com.bytedesk.core.utils.Utils; -@SpringBootTest -@ActiveProfiles("test") -public class DepartmentServiceTests { +// @SpringBootTest +// @ActiveProfiles("test") +// public class DepartmentServiceTests { - @Autowired - private DepartmentService departmentService; +// @Autowired +// private DepartmentService departmentService; - @MockBean - private ModelMapper modelMapper; +// @MockBean +// private ModelMapper modelMapper; - @MockBean - private AuthService authService; +// @MockBean +// private AuthService authService; - @MockBean - private OrganizationService organizationService; +// @MockBean +// private OrganizationService organizationService; - @MockBean - private DepartmentRepository departmentRepository; +// @MockBean +// private DepartmentRepository departmentRepository; - private DepartmentRequest departmentRequest; - private User user; - private Organization organization; +// private DepartmentRequest departmentRequest; +// private User user; +// private Organization organization; - @BeforeEach - public void setUp() { - departmentRequest = new DepartmentRequest(); - departmentRequest.setDescription("Test department"); - departmentRequest.setParent_did("parentDid"); - departmentRequest.setOrg_oid("f01f5444ecbc437cb5b8de7ca7dd023c"); - departmentRequest.setId(1L); - departmentRequest.setDid(Utils.getUid()); - departmentRequest.setNickname("Test Department"); - departmentRequest.setAvatar("testAvatar"); +// @BeforeEach +// public void setUp() { +// departmentRequest = new DepartmentRequest(); +// departmentRequest.setDescription("Test department"); +// departmentRequest.setParent_did("parentDid"); +// departmentRequest.setOrgOid("f01f5444ecbc437cb5b8de7ca7dd023c"); +// departmentRequest.setId(1L); +// departmentRequest.setDid(Utils.getUid()); +// departmentRequest.setNickname("Test Department"); +// departmentRequest.setAvatar("testAvatar"); - user = new User(); - user.setUsername("testUser"); +// user = new User(); +// user.setUsername("testUser"); - organization = new Organization(); +// organization = new Organization(); - when(authService.getCurrentUser()).thenReturn(user); - when(organizationService.findByOid(departmentRequest.getOrg_oid())).thenReturn(Optional.of(organization)); - when(modelMapper.map(departmentRequest, Department.class)).thenAnswer(invocation -> { - Department dept = new Department(); - dept.setDid(departmentRequest.getDid()); - dept.setOrganization(organization); - dept.setUser(user); - return dept; - }); - } +// when(authService.getCurrentUser()).thenReturn(user); +// when(organizationService.findByOid(departmentRequest.getOrgOid())).thenReturn(Optional.of(organization)); +// when(modelMapper.map(departmentRequest, Department.class)).thenAnswer(invocation -> { +// Department dept = new Department(); +// dept.setDid(departmentRequest.getDid()); +// dept.setOrganization(organization); +// // dept.setUser(user); +// return dept; +// }); +// } - @Test - public void testCreateDepartmentWithValidRequest() { - // Act - Department createdDepartment = departmentService.create(departmentRequest); +// @Test +// public void testCreateDepartmentWithValidRequest() { +// // Act +// Department createdDepartment = departmentService.create(departmentRequest); - // Assert - assertNotNull(createdDepartment); - assertEquals(departmentRequest.getDid(), createdDepartment.getDid()); - assertEquals(organization, createdDepartment.getOrganization()); - assertEquals(user, createdDepartment.getUser()); +// // Assert +// assertNotNull(createdDepartment); +// assertEquals(departmentRequest.getDid(), createdDepartment.getDid()); +// assertEquals(organization, createdDepartment.getOrganization()); +// // assertEquals(user, createdDepartment.getUser()); - // Verify that the repository's save method is called - // verify(departmentRepository).save(any(Department.class)); - } +// // Verify that the repository's save method is called +// // verify(departmentRepository).save(any(Department.class)); +// } - @Test - public void testCreateDepartmentWithNonExistingOrgOid() { - // Arrange - when(organizationService.findByOid(departmentRequest.getOrg_oid())).thenReturn(Optional.empty()); +// @Test +// public void testCreateDepartmentWithNonExistingOrgOid() { +// // Arrange +// when(organizationService.findByOid(departmentRequest.getOrgOid())).thenReturn(Optional.empty()); - // Act & Assert - // Department createdDepartment = departmentService.create(departmentRequest); - // assertNull(createdDepartment); - } -} \ No newline at end of file +// // Act & Assert +// // Department createdDepartment = departmentService.create(departmentRequest); +// // assertNull(createdDepartment); +// } +// } \ No newline at end of file diff --git a/modules/team/src/test/java/com/bytedesk/team/OrganizationServiceTests.java b/modules/team/src/test/java/com/bytedesk/team/OrganizationServiceTests.java index 4c4b304930..8f27b052c7 100644 --- a/modules/team/src/test/java/com/bytedesk/team/OrganizationServiceTests.java +++ b/modules/team/src/test/java/com/bytedesk/team/OrganizationServiceTests.java @@ -20,7 +20,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import com.bytedesk.core.auth.AuthService; + +import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.team.organization.OrganizationRepository; import com.bytedesk.team.organization.OrganizationService; diff --git a/starter/.DS_Store b/starter/.DS_Store index 84509558ab..3403cb3c9f 100644 Binary files a/starter/.DS_Store and b/starter/.DS_Store differ diff --git a/starter/.gitignore b/starter/.gitignore index 06285d428c..549e00a2a9 100644 --- a/starter/.gitignore +++ b/starter/.gitignore @@ -18,7 +18,6 @@ target/ *.iws *.iml *.ipr -application-prod.properties ### NetBeans ### /nbproject/private/ diff --git a/starter/h2db/weiyuim.mv.db b/starter/h2db/weiyuim.mv.db new file mode 100644 index 0000000000..33d2fc920d Binary files /dev/null and b/starter/h2db/weiyuim.mv.db differ diff --git a/starter/pom.xml b/starter/pom.xml index f25d2dee3c..6c4fc49ec6 100644 --- a/starter/pom.xml +++ b/starter/pom.xml @@ -7,9 +7,7 @@ com.bytedesk im - 0.0.1-SNAPSHOT - ../pom.xml starter @@ -51,11 +49,6 @@ spring-boot-starter-data-jpa - - com.mysql - mysql-connector-j - - org.springframework.boot spring-boot-starter-validation @@ -73,15 +66,6 @@ spring-boot-starter-cache - - org.springframework.boot - spring-boot-starter-data-redis - - - - org.springframework.session - spring-session-data-redis - org.springframework.boot @@ -98,71 +82,100 @@ spring-boot-starter-quartz - + - - - + + + + + + + + + + com.h2database + h2 + runtime + + + + com.bytedesk - weiyu-ai - ${im.version} - - - - - com.bytedesk - weiyu-local + im-ai ${im.version} com.bytedesk - weiyu-core + im-core ${im.version} com.bytedesk - weiyu-team + im-team ${im.version} - - com.bytedesk - weiyu-service + im-service ${im.version} + + + com.bytedesk - weiyu-socket + im-socket + ${im.version} + + + + + + + com.bytedesk + im-local ${im.version} - + + + + + + com.alibaba.fastjson2 fastjson2 - 2.0.45 + 2.0.48 @@ -217,13 +230,6 @@ 4.1.106.Final - - - cn.hutool - hutool-crypto - 5.8.25 - - com.google.code.gson @@ -253,14 +259,30 @@ 1.4 - - + + + + com.google.guava + guava + 33.1.0-jre + + + + + + + + org.projectlombok diff --git a/starter/src/.DS_Store b/starter/src/.DS_Store index 1d60f0ed63..43ed213a2c 100644 Binary files a/starter/src/.DS_Store and b/starter/src/.DS_Store differ diff --git a/starter/src/main/java/com/bytedesk/starter/StarterApplication.java b/starter/src/main/java/com/bytedesk/starter/StarterApplication.java index 01278a971a..c6c081f36e 100644 --- a/starter/src/main/java/com/bytedesk/starter/StarterApplication.java +++ b/starter/src/main/java/com/bytedesk/starter/StarterApplication.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 15:02:58 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 18:45:38 + * @LastEditTime: 2024-04-17 10:35:28 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -16,20 +16,14 @@ package com.bytedesk.starter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -// import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -// @Slf4j -@EnableAsync -// @EnableCaching // 迁移到cacheconfig @EnableScheduling @EnableJpaAuditing @ComponentScan("com.bytedesk.*") @SpringBootApplication -// @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60 * 60 * 24 * 7) public class StarterApplication { public static void main(String[] args) { diff --git a/starter/src/main/java/com/bytedesk/starter/config/CorsConfig.java b/starter/src/main/java/com/bytedesk/starter/config/CorsConfig.java new file mode 100644 index 0000000000..98a92096ba --- /dev/null +++ b/starter/src/main/java/com/bytedesk/starter/config/CorsConfig.java @@ -0,0 +1,69 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-04-19 11:39:40 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-19 13:40:26 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 技术/商务联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.starter.config; + +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +// import lombok.extern.slf4j.Slf4j; + +// @Slf4j +@Configuration +public class CorsConfig { + + /** + * 经测试:仅有此处起作用,corsFilter()和WebMvcConfig.addCorsMappings()不起作用 + * @return + */ + // https://docs.spring.io/spring-security/reference/reactive/integrations/cors.html + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + // configuration.setAllowCredentials(true); // 不能启用 + configuration.setAllowedOrigins(List.of("*")); // 必须启用 + // configuration.setAllowedOriginPatterns(List.of("*")); // 不能启用 + configuration.setAllowedMethods(List.of("*")); + configuration.setAllowedHeaders(List.of("*")); + // configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + // configuration.setAllowedHeaders(List.of("Authorization", "Content-Type")); + // + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + +// @Bean +// public CorsFilter corsFilter() { +// log.info("CorsConfig.corsFilter()"); +// CorsConfiguration corsConfiguration = new CorsConfiguration(); +// //1,允许任何来源 +// corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*")); +// //2,允许任何请求头 +// corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); +// //3,允许任何方法 +// corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); +// //4,允许凭证 +// corsConfiguration.setAllowCredentials(true); +// // +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// source.registerCorsConfiguration("/**", corsConfiguration); +// // +// return new CorsFilter(source); +// } +} \ No newline at end of file diff --git a/starter/src/main/java/com/bytedesk/starter/config/SecurityConfig.java b/starter/src/main/java/com/bytedesk/starter/config/SecurityConfig.java index c79e988279..d0dce0e455 100644 --- a/starter/src/main/java/com/bytedesk/starter/config/SecurityConfig.java +++ b/starter/src/main/java/com/bytedesk/starter/config/SecurityConfig.java @@ -1,7 +1,19 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-01-29 16:17:36 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2024-04-27 10:13:08 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * 仅支持企业内部员工自用,严禁私自用于销售、二次销售或者部署SaaS方式销售 + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ package com.bytedesk.starter.config; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,14 +27,10 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - import static org.springframework.security.config.Customizer.withDefaults; -import com.bytedesk.core.auth.AuthEntryPoint; -import com.bytedesk.core.auth.AuthJwtTokenFilter; +import com.bytedesk.core.rbac.auth.AuthEntryPoint; +import com.bytedesk.core.rbac.auth.AuthJwtTokenFilter; import com.bytedesk.core.rbac.user.UserDetailsServiceImpl; /** @@ -46,9 +54,9 @@ public class SecurityConfig { // http .cors(withDefaults()) - // .cors(cors -> cors.disable()) .csrf(csrf -> csrf.disable()) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) + // based on token, dont need session .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/**").authenticated() @@ -58,8 +66,6 @@ public class SecurityConfig { .authenticationProvider(authenticationProvider()) // .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults())) .addFilterBefore(authJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); - // .httpBasic(withDefaults()) - // .formLogin(withDefaults()); // return http.build(); } @@ -87,24 +93,5 @@ public class SecurityConfig { return authenticationProvider; } - // https://docs.spring.io/spring-security/reference/reactive/integrations/cors.html - @Bean - CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(List.of("*")); - configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - configuration.setAllowedHeaders(List.of("Authorization", "Content-Type")); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } - - // @Bean - // public WebSecurityCustomizer webSecurityCustomizer() { - // return (web) -> web.ignoring() - // // .requestMatchers(HttpMethod.OPTIONS, "**", "/**", "/stomp/**") - // .requestMatchers("**") - // ; - // } } diff --git a/starter/src/main/java/com/bytedesk/starter/config/WebMvcConfig.java b/starter/src/main/java/com/bytedesk/starter/config/WebMvcConfig.java index 68a1f4bbb4..9450ed2d67 100644 --- a/starter/src/main/java/com/bytedesk/starter/config/WebMvcConfig.java +++ b/starter/src/main/java/com/bytedesk/starter/config/WebMvcConfig.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-26 15:28:57 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 13:16:31 + * @LastEditTime: 2024-04-23 21:02:53 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -14,18 +14,17 @@ */ package com.bytedesk.starter.config; -import org.springframework.beans.factory.annotation.Autowired; // import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; +// import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.bytedesk.core.config.BytedeskProperties; +// import com.bytedesk.core.config.BytedeskProperties; -import lombok.extern.slf4j.Slf4j; +// import lombok.extern.slf4j.Slf4j; -@Slf4j +// @Slf4j @Configuration public class WebMvcConfig implements WebMvcConfigurer { @@ -35,9 +34,8 @@ public class WebMvcConfig implements WebMvcConfigurer { // @Value("${bytedesk.upload-dir}") // private static String uploadDir; - @Autowired - private BytedeskProperties bytedeskProperties; - + // @Autowired + // private BytedeskProperties bytedeskProperties; private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", @@ -50,23 +48,27 @@ public class WebMvcConfig implements WebMvcConfigurer { /** * 跨域配置: 允许跨域访问 + * 在 CorsConfig.java中配置,此处没有必要? */ // https://spring.io/guides/gs/rest-service-cors - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedMethods("*") - .allowedOrigins(bytedeskProperties.getCorsAllowedOrigins()); - } + // @Override + // public void addCorsMappings(CorsRegistry registry) { + // // + // registry.addMapping("/**") + // .allowedMethods("*") + // .allowedOriginPatterns("*") + // // allow cookies + // .allowCredentials(true); + // } /** * 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - for (String path : CLASSPATH_RESOURCE_LOCATIONS) { - log.info("CLASSPATH_RESOURCE_LOCATIONS: {}", path); - } + // for (String path : CLASSPATH_RESOURCE_LOCATIONS) { + // log.info("CLASSPATH_RESOURCE_LOCATIONS: {}", path); + // } /* 配置server虚拟路径,handler为前台访问的URL目录,locations为files相对应的本地路径 也就是说如果有一个 upload/avatar/aaa.png 请求,那程序会到后面的目录里面找aaa.png文件 diff --git a/starter/src/main/java/com/bytedesk/starter/listener/ImEventListener.java b/starter/src/main/java/com/bytedesk/starter/listener/ImEventListener.java index 457a436dc7..224ac5ef06 100644 --- a/starter/src/main/java/com/bytedesk/starter/listener/ImEventListener.java +++ b/starter/src/main/java/com/bytedesk/starter/listener/ImEventListener.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:17:36 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-27 13:20:00 + * @LastEditTime: 2024-04-15 12:13:52 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. diff --git a/starter/src/main/java/com/bytedesk/starter/runner/InitDataRunner.java b/starter/src/main/java/com/bytedesk/starter/runner/InitDataRunner.java index c012e52f86..adbe975fc5 100644 --- a/starter/src/main/java/com/bytedesk/starter/runner/InitDataRunner.java +++ b/starter/src/main/java/com/bytedesk/starter/runner/InitDataRunner.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-01-29 16:17:36 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2024-03-29 13:40:05 + * @LastEditTime: 2024-04-28 11:16:55 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -21,9 +21,15 @@ import org.springframework.stereotype.Component; import com.bytedesk.ai.llm.LlmService; import com.bytedesk.ai.robot.RobotService; +import com.bytedesk.core.asistant.AsistantService; +import com.bytedesk.core.channel.ChannelService; +import com.bytedesk.core.rbac.authority.AuthorityService; import com.bytedesk.core.rbac.role.RoleService; import com.bytedesk.core.rbac.user.UserService; +import com.bytedesk.core.thread.ThreadService; import com.bytedesk.core.upload.UploadService; +import com.bytedesk.service.agent.AgentService; +import com.bytedesk.service.workgroup.WorkgroupService; import com.bytedesk.team.department.DepartmentService; import com.bytedesk.team.member.MemberService; import com.bytedesk.team.organization.OrganizationService; @@ -39,6 +45,9 @@ import lombok.extern.slf4j.Slf4j; @Component public class InitDataRunner implements ApplicationRunner { + @Autowired + AuthorityService authorityService; + @Autowired RoleService roleService; @@ -60,21 +69,38 @@ public class InitDataRunner implements ApplicationRunner { @Autowired RobotService robotService; + @Autowired + AgentService agentService; + + @Autowired + WorkgroupService workgroupService; + @Autowired UploadService uploadService; + @Autowired + AsistantService asistantService; + + @Autowired + ChannelService channelService; + + @Autowired + ThreadService threadService; + @Override public void run(ApplicationArguments args) throws Exception { log.debug("application started, init Default data, dont change the init order"); + authorityService.initData(); + roleService.initData(); userService.initData(); - roleService.updateInitData(); - organizationService.initData(); + roleService.updateInitData(); + departmentService.initData(); memberService.initData(); @@ -83,7 +109,17 @@ public class InitDataRunner implements ApplicationRunner { robotService.initData(); + agentService.initData(); + + workgroupService.initData(); + uploadService.initUploadDir(); + + asistantService.initData(); + + channelService.initData(); + + threadService.initData(); } } diff --git a/starter/src/main/resources/application-dev.properties b/starter/src/main/resources/application-dev-h2.properties similarity index 88% rename from starter/src/main/resources/application-dev.properties rename to starter/src/main/resources/application-dev-h2.properties index b9ee82e969..b84322c80d 100644 --- a/starter/src/main/resources/application-dev.properties +++ b/starter/src/main/resources/application-dev-h2.properties @@ -5,12 +5,15 @@ bytedesk.debug=true # default admin username/password/email/mobile info # 默认管理员用户名密码 -bytedesk.username=admin +bytedesk.username=admin@email.com bytedesk.password=admin +bytedesk.nickname=Administrator bytedesk.email=admin@email.com -bytedesk.mobile=13311156272 +# system use mobile number 18888888000~18888888999 as test mobile number, verification code is 123456 +# 系统默认使用 18888888000~18888888999 作为测试手机号,验证码为123456 +bytedesk.mobile=18888888000 bytedesk.company=MyCompany -bytedesk.timezone=GMT+8 +# bytedesk.timezone=GMT+8 # =============================== #=bytedesk cors config @@ -20,7 +23,9 @@ bytedesk.cors-allowed-origins=* # =============================== #=bytedesk jwt config # =============================== -bytedesk.jwt-secret-key=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970 +# please change jwt secret key to your own +# use this: https://www.browserling.com/tools/random-hex +bytedesk.jwt-secret-key=1dfaf8d004207b628a9a6b859c429f49a9a7ead9fd8161c1e60847aeef06dbd2 # 2592000000 # 30 days, milliseconds bytedesk.jwt-expiration=2592000000 # 5184000000 # 60 days, milliseconds @@ -30,8 +35,8 @@ bytedesk.jwt-refresh-token-expiration=5184000000 #=bytedesk cache config # =============================== # 0: no cache, 1: caffeine cache, 2: caffeine + redis cache -bytedesk.cache-level=2 -bytedesk.cache-prefix=bytedeskim +# bytedesk.cache-level=2 +# bytedesk.cache-prefix=bytedeskim # cache.caffeine.maximumSize=1000 # cache.caffeine.expireAfterWriteSeconds=5 @@ -89,7 +94,7 @@ upload.dir.prefix= # =============================== # = 阿里云OSS访问密钥 # =============================== -aliyun.region-id=cn-hangzhou +aliyun.region-id=cn # aliyun.access-key-id=placeholder aliyun.access-key-secret=placeholder @@ -97,9 +102,9 @@ aliyun.access-key-secret=placeholder # 阿里云OSS服务相关配置 # OSS的endpoint,这里是华南地区(也就是深圳) aliyun.oss-endpoint=https://oss-cn-shenzhen.aliyuncs.com -aliyun.oss-base-url=https://bytedesk.oss-cn-shenzhen.aliyuncs.com +aliyun.oss-base-url=https://oss-cn-shenzhen.aliyuncs.com # 这是创建的bucket -aliyun.oss-bucket-name=bytedesk +aliyun.oss-bucket-name=123 # 这里已经把自己的域名映射到bucket地址了。需要设置域名绑定,设置域名CNAME(暂不使用) aliyun.oss-img-domain= @@ -115,7 +120,7 @@ aliyun.sms.templatecode= # =============================== # 创建bucket并需要在此bucket下创建文件夹:apns/development(二级文件夹), apns/production(二级文件夹), avatars, images, voices, files # 存储桶所属地域 -tencent.bucket.location=ap-shanghai +tencent.bucket.location=ap # 存储桶名称 tencent.bucket.name= # 访问域名 @@ -150,21 +155,32 @@ logging.file.max-history=10 #=spring-boot-starter-data-jpa # https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.data # =============================== -spring.datasource.url=jdbc:mysql://127.0.0.1:3306/bytedesk_im -spring.datasource.username=root -spring.datasource.password=C8aJEVCCvSA1VFi8 -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# http://localhost:9003/h2-console +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=false # +# spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.url=jdbc:h2:file:./h2db/weiyuim +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=sa +# +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=update -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.database=mysql -spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect +spring.jpa.show-sql=false +spring.jpa.properties.hibernate.format_sql=false +spring.jpa.defer-datasource-initialization=true spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true +# https://www.cnblogs.com/suizhikuo/p/16997225.html +spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +spring.jackson.time-zone=GMT+8 # =============================== #=spring-boot-starter-cache # https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.cache +# https://docs.spring.io/spring-framework/reference/integration/cache/annotations.html#cache-spel-context # =============================== # disable caching: none, https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#io.caching.provider.none spring.cache.type=none @@ -183,8 +199,7 @@ spring.cache.redis.key-prefix=bytedeskim: spring.data.redis.database=0 spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 -spring.data.redis.password=C8aJEVCCvSA1VFi8 - +spring.data.redis.password=123 # =============================== #=spring-boot-starter-data-rest diff --git a/starter/src/main/resources/application.properties b/starter/src/main/resources/application.properties index d1df27bbaa..f59bef126d 100644 --- a/starter/src/main/resources/application.properties +++ b/starter/src/main/resources/application.properties @@ -3,6 +3,7 @@ # Server Properties #=https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server # =============================== +# server.host=127.0.0.1 server.port=9003 # =============================== @@ -15,6 +16,12 @@ spring.freemarker.suffix=.ftl # =============================== #=profiles +# 注意:切换不同profiles时,需要同步修改pom.xml切换driver驱动 # =============================== -spring.profiles.active=dev -# spring.profiles.active=prod +# for mysql/redis +# spring.profiles.active=dev-mysql +# open source - only using h2database/caffeine, no dependency on middle ware +# http://localhost:9003/h2-console +spring.profiles.active=dev-h2 +# for posgresql +# spring.profiles.active=dev-pg diff --git a/starter/src/main/resources/lib/readme.md b/starter/src/main/resources/lib/readme.md new file mode 100644 index 0000000000..8ab5c8f179 --- /dev/null +++ b/starter/src/main/resources/lib/readme.md @@ -0,0 +1,18 @@ + + +# lib + +- put service-xx.jar and socket-xx.jar in lib dir