zoujiajian преди 2 години
родител
ревизия
646f4ea53f
променени са 73 файла, в които са добавени 5453 реда и са изтрити 5 реда
  1. 246 0
      luma/pom.xml
  2. 69 0
      luma/src/main/java/com/yhlxj/Application.java
  3. 40 0
      luma/src/main/java/com/yhlxj/dao/MyMetaObjectHandler.java
  4. 37 0
      luma/src/main/java/com/yhlxj/dao/MybatisPlusConfig.java
  5. 13 0
      luma/src/main/java/com/yhlxj/dao/mapper/AccountMapper.java
  6. 12 0
      luma/src/main/java/com/yhlxj/dao/mapper/GoodsDonMapper.java
  7. 12 0
      luma/src/main/java/com/yhlxj/dao/mapper/GoodsDonSkuMapper.java
  8. 11 0
      luma/src/main/java/com/yhlxj/dao/mapper/GroupsMapper.java
  9. 12 0
      luma/src/main/java/com/yhlxj/dao/mapper/GroupsRelationMapper.java
  10. 16 0
      luma/src/main/java/com/yhlxj/dao/mapper/UserBindDetailMapper.java
  11. 78 0
      luma/src/main/java/com/yhlxj/dao/mapper/UserMapper.java
  12. 11 0
      luma/src/main/java/com/yhlxj/dao/mapper/midjourney/LumaUserConversationMapper.java
  13. 19 0
      luma/src/main/java/com/yhlxj/dao/mapper/midjourney/LumaUserMapper.java
  14. 36 0
      luma/src/main/java/com/yhlxj/dao/model/BaseEntity.java
  15. 35 0
      luma/src/main/java/com/yhlxj/dao/model/dto/MessageButton.java
  16. 106 0
      luma/src/main/java/com/yhlxj/dao/model/entity/Account.java
  17. 248 0
      luma/src/main/java/com/yhlxj/dao/model/entity/GoodsDon.java
  18. 210 0
      luma/src/main/java/com/yhlxj/dao/model/entity/GoodsDonSku.java
  19. 138 0
      luma/src/main/java/com/yhlxj/dao/model/entity/GroupsRelation.java
  20. 175 0
      luma/src/main/java/com/yhlxj/dao/model/entity/GroupsTrips.java
  21. 58 0
      luma/src/main/java/com/yhlxj/dao/model/entity/LumaUser.java
  22. 94 0
      luma/src/main/java/com/yhlxj/dao/model/entity/LumaUserConversation.java
  23. 128 0
      luma/src/main/java/com/yhlxj/dao/model/entity/User.java
  24. 25 0
      luma/src/main/java/com/yhlxj/dao/model/entity/UserBindDetail.java
  25. 55 0
      luma/src/main/java/com/yhlxj/dao/model/enums/WsMessageTypeEnum.java
  26. 34 0
      luma/src/main/java/com/yhlxj/dao/model/response/SubmitResult.java
  27. 65 0
      luma/src/main/java/com/yhlxj/dao/model/response/VideoTask.java
  28. 769 0
      luma/src/main/java/com/yhlxj/redis/RedisService.java
  29. 38 0
      luma/src/main/java/com/yhlxj/service/common/EnvCommonService.java
  30. 37 0
      luma/src/main/java/com/yhlxj/service/midjourney/LumaService.java
  31. 475 0
      luma/src/main/java/com/yhlxj/service/midjourney/impl/LumaServiceImpl.java
  32. 19 0
      luma/src/main/java/com/yhlxj/service/user/UserBindRelationService.java
  33. 87 0
      luma/src/main/java/com/yhlxj/service/user/impl/UserBindRelationServiceImpl.java
  34. 67 0
      luma/src/main/java/com/yhlxj/util/COSUtil.java
  35. 110 0
      luma/src/main/java/com/yhlxj/util/SpringCtxUtils.java
  36. 87 0
      luma/src/main/java/com/yhlxj/util/VideoCoverExtractor.java
  37. 127 0
      luma/src/main/java/com/yhlxj/web/GlobalExceptionHandler.java
  38. 52 0
      luma/src/main/java/com/yhlxj/web/config/JacksonConfig.java
  39. 19 0
      luma/src/main/java/com/yhlxj/web/config/WebSocketConfiguration.java
  40. 256 0
      luma/src/main/java/com/yhlxj/web/mirror/LumaController.java
  41. 165 0
      luma/src/main/java/com/yhlxj/web/wss/LumaServerEndpoint.java
  42. 13 0
      luma/src/main/java/com/yhlxj/web/wss/WssSendable.java
  43. 143 0
      luma/src/main/java/com/yhlxj/web/wss/WssSession.java
  44. 0 0
      luma/src/main/resources/application-dev.yml
  45. 0 0
      luma/src/main/resources/application-prd.yml
  46. 106 0
      luma/src/main/resources/application-pre.yml
  47. 4 0
      luma/src/main/resources/application.yml
  48. 111 0
      luma/src/main/resources/config/logback-debug.xml
  49. 105 0
      luma/src/main/resources/config/logback.xml
  50. 12 0
      netflix-dao/src/main/java/com/cyksj/mapper/LumaUserMapper.java
  51. 42 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitVideoDTO.java
  52. 21 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/BlendDimensions.java
  53. 35 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/MessageButton.java
  54. 107 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/MidjourneyProxyAccountInfo.java
  55. 40 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/MjVersionStatus.java
  56. 33 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitActionDTO.java
  57. 34 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitBlendDTO.java
  58. 28 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitDescribeDTO.java
  59. 9 1
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitImagineDTO.java
  60. 32 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitModalDTO.java
  61. 28 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitSeedDTO.java
  62. 28 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitShortenDTO.java
  63. 20 0
      netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitUploadDTO.java
  64. 53 0
      netflix-dao/src/main/java/com/cyksj/model/entity/LumaUser.java
  65. 82 0
      netflix-dao/src/main/java/com/cyksj/model/views/LumaUserConversationView.java
  66. 28 0
      netflix-dao/src/main/java/com/cyksj/model/views/LumaUserDetailView.java
  67. 40 0
      netflix-dao/src/main/java/com/cyksj/model/views/LumaUserView.java
  68. 1 0
      netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java
  69. 0 2
      netflix-web/src/main/java/com/cyksj/web/controller/mirror/MirrorController.java
  70. 0 0
      netflix-web/src/main/resources/application-dev.yml
  71. 0 0
      netflix-web/src/main/resources/application-prd.yml
  72. 5 1
      netflix-web/src/main/resources/application-pre.yml
  73. 2 1
      pom.xml

+ 246 - 0
luma/pom.xml

@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.4.3</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <groupId>com.netflix</groupId>
+    <artifactId>luma</artifactId>
+    <packaging>jar</packaging>
+
+
+    <properties>
+        <java.version>11</java.version>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <io-undertow.version>2.0.30.Final</io-undertow.version>
+        <xnio-nio.version>3.7.7.Final</xnio-nio.version>
+    </properties>
+
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-json</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.hibernate.validator</groupId>
+                    <artifactId>hibernate-validator</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.undertow</groupId>
+                    <artifactId>undertow-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.undertow</groupId>
+                    <artifactId>undertow-servlet</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.xnio</groupId>
+            <artifactId>xnio-nio</artifactId>
+            <version>${xnio-nio.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-core</artifactId>
+            <version>${io-undertow.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.jboss.xnio</groupId>
+                    <artifactId>xnio-nio</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.jboss.xnio</groupId>
+                    <artifactId>xnio-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-servlet</artifactId>
+            <version>${io-undertow.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.undertow</groupId>
+                    <artifactId>undertow-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.netflix</groupId>
+            <artifactId>netflix-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>mysql</groupId>
+                    <artifactId>mysql-connector-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcprov-jdk15on</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.zxing</groupId>
+                    <artifactId>core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-test</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.alipay.sdk</groupId>
+                    <artifactId>alipay-sdk-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-aop</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.tencentcloudapi</groupId>
+                    <artifactId>tencentcloud-sdk-java-sms</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.tencentcloudapi</groupId>
+                    <artifactId>tencentcloud-sdk-java-faceid</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.tencentcloudapi</groupId>
+                    <artifactId>tencentcloud-sdk-java</artifactId>
+                </exclusion>
+            </exclusions>
+            <version>1.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.twelvemonkeys.imageio</groupId>
+            <artifactId>imageio-webp</artifactId>
+            <version>3.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.8</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <version>5.6.34</version>
+        </dependency>
+        <dependency>
+            <groupId>com.netflix</groupId>
+            <artifactId>netflix-dao</artifactId>
+            <version>1.0</version>
+            <scope>compile</scope>
+        </dependency>
+
+         <dependency>
+                <groupId>org.bytedeco</groupId>
+                <artifactId>javacv</artifactId>
+                <version>1.5.8</version>
+            </dependency>
+            <dependency>
+                <groupId>org.bytedeco</groupId>
+                <artifactId>ffmpeg-platform</artifactId>
+                <version>5.1.2-1.5.8</version>
+            </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>${start-class}</mainClass>
+                    <layout>ZIP</layout><!-- Replace with your main class -->
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
+                </configuration>
+            </plugin>
+        </plugins>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+
+        <finalName>luma</finalName>
+    </build>
+
+</project>

+ 69 - 0
luma/src/main/java/com/yhlxj/Application.java

@@ -0,0 +1,69 @@
+package com.yhlxj;
+
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import io.undertow.UndertowOptions;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+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.Jackson2JsonRedisSerializer;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
+
+@SpringBootApplication
+@EnableScheduling
+public class Application {
+
+	public static void main(String[] args) {
+		SpringApplication.run(Application.class, args);
+	}
+
+
+	@Bean
+	public MethodValidationPostProcessor methodValidationPostProcessor (){
+		return new MethodValidationPostProcessor();
+	}
+
+	@Bean
+	public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
+		StringRedisTemplate template = new StringRedisTemplate(factory);
+		//定义key序列化方式
+		//RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型会出现异常信息;需要我们上面的自定义key生成策略,一般没必要
+		//定义value的序列化方式
+		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+		ObjectMapper om = new ObjectMapper();
+		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+		om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
+				ObjectMapper.DefaultTyping.NON_FINAL,
+				JsonTypeInfo.As.WRAPPER_ARRAY);
+		jackson2JsonRedisSerializer.setObjectMapper(om);
+
+		// template.setKeySerializer(redisSerializer);
+		template.setValueSerializer(jackson2JsonRedisSerializer);
+		template.setHashValueSerializer(jackson2JsonRedisSerializer);
+		template.afterPropertiesSet();
+		return template;
+	}
+
+	/**
+	 * undertow 容器配置
+	 */
+	@Bean
+	public UndertowServletWebServerFactory factory() {
+		UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
+		// 开启http2 开启keep-alive 允许cookie最大化
+		factory.addBuilderCustomizers(builder ->
+				builder.setServerOption(UndertowOptions.ENABLE_HTTP2, false).
+						setServerOption(UndertowOptions.ALLOW_EQUALS_IN_COOKIE_VALUE, true).
+						setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, true));
+		return factory;
+	}
+}

+ 40 - 0
luma/src/main/java/com/yhlxj/dao/MyMetaObjectHandler.java

@@ -0,0 +1,40 @@
+package com.yhlxj.dao;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * @author chan
+ * @className MyMetaObjectHandler
+ * @description
+ * @date 2020/7/29 2:44 下午
+ */
+@Component
+public class MyMetaObjectHandler implements MetaObjectHandler {
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        //根据属性名称设置值
+        this.setFieldValByName("createdTime",new Date(),metaObject);
+        this.setFieldValByName("updateTime",new Date(),metaObject);
+        //this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
+        //this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
+        /* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */
+        //this.setFieldValByName("operator", "Jerry", metaObject);
+        //this.setInsertFieldValByName("operator", "Jerry", metaObject);
+
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+
+        this.setFieldValByName("updateTime", new Date(), metaObject);
+        //this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
+        //this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
+        /* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */
+        //this.setFieldValByName("operator", "Tom", metaObject);
+        //this.setUpdateFieldValByName("operator", "Tom", metaObject);
+    }
+}

+ 37 - 0
luma/src/main/java/com/yhlxj/dao/MybatisPlusConfig.java

@@ -0,0 +1,37 @@
+package com.yhlxj.dao;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * @author chan
+ * @className MybatisConfig
+ * @date 2020/7/15 3:05 下午
+ */
+
+@EnableTransactionManagement
+@Configuration
+@MapperScan(basePackages = {"com.yhlxj.dao.mapper"})
+public class MybatisPlusConfig {
+
+    /**
+     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
+        return interceptor;
+    }
+
+    @Bean
+    public ConfigurationCustomizer configurationCustomizer() {
+        return configuration -> configuration.setUseDeprecatedExecutor(false);
+    }
+}

+ 13 - 0
luma/src/main/java/com/yhlxj/dao/mapper/AccountMapper.java

@@ -0,0 +1,13 @@
+package com.yhlxj.dao.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.Account;
+
+/**
+ * @author chan
+ * @date 2022/9/27 6:22 PM
+ */
+public interface AccountMapper extends BaseMapper<Account> {
+
+}

+ 12 - 0
luma/src/main/java/com/yhlxj/dao/mapper/GoodsDonMapper.java

@@ -0,0 +1,12 @@
+package com.yhlxj.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.GoodsDon;
+
+/**
+ * @author chan
+ * @date 2022/9/27 5:31 PM
+ */
+public interface GoodsDonMapper extends BaseMapper<GoodsDon> {
+
+}

+ 12 - 0
luma/src/main/java/com/yhlxj/dao/mapper/GoodsDonSkuMapper.java

@@ -0,0 +1,12 @@
+package com.yhlxj.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.GoodsDonSku;
+
+/**
+ * @author chan
+ * @date 2022/9/26 6:54 PM
+ */
+public interface GoodsDonSkuMapper  extends BaseMapper<GoodsDonSku> {
+
+}

+ 11 - 0
luma/src/main/java/com/yhlxj/dao/mapper/GroupsMapper.java

@@ -0,0 +1,11 @@
+package com.yhlxj.dao.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.GroupsTrips;
+
+
+/**
+ * @author chan
+ * @date 2022/9/27 4:24 PM
+ */
+public interface GroupsMapper extends BaseMapper<GroupsTrips> {
+}

+ 12 - 0
luma/src/main/java/com/yhlxj/dao/mapper/GroupsRelationMapper.java

@@ -0,0 +1,12 @@
+package com.yhlxj.dao.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.GroupsRelation;
+
+/**
+ * @author chan
+ * @date 2022/9/27 11:10 AM
+ */
+public interface GroupsRelationMapper extends BaseMapper<GroupsRelation> {
+}

+ 16 - 0
luma/src/main/java/com/yhlxj/dao/mapper/UserBindDetailMapper.java

@@ -0,0 +1,16 @@
+package com.yhlxj.dao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.UserBindDetail;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: UserBindDetailMapper
+ *创建者: JavaZou
+ *创建时间:2022/12/29 13:54
+ */
+public interface UserBindDetailMapper extends BaseMapper<UserBindDetail> {
+	List<String> selectPhones(Integer filter);
+}

+ 78 - 0
luma/src/main/java/com/yhlxj/dao/mapper/UserMapper.java

@@ -0,0 +1,78 @@
+package com.yhlxj.dao.mapper;
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.User;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2021/10/10 下午3:28
+ */
+public interface UserMapper extends BaseMapper<User> {
+
+    @Update("update user set lottery_time = lottery_time - 1 where id = #{userId} and lottery_time > 0")
+    int decrLotteryTime(@Param("userId") Long userId);
+
+    @Update("update user set lottery_time = lottery_time + 1 where id = #{id}")
+    void incrLotteryTime(@Param("id") Long userId);
+
+    @Update("update user set points = points - #{points} where id = #{id} and points >= #{points} and points > 0")
+    Integer subPoints(@Param("id") Long id, @Param("points") BigDecimal points);
+
+    @Update("update user set points = points + #{addPoints} where id = #{id} and points = #{originalPoints}")
+    Integer updatePoints(@Param("id") Long id, @Param("originalPoints") BigDecimal originalPoints, @Param("addPoints") BigDecimal addPoints);
+
+    @Update("update user set active_points = active_points - #{subPoints} where active_points > 0 and active_points >= #{subPoints} and id = #{userId}")
+    Integer subActivePoints(@Param("userId") Long userId, @Param("subPoints") BigDecimal subPoints);
+
+    @Select("select id from user where nickname like concat(#{nickname},'%')")
+    List<Long> selectListByNickname(String nickname);
+
+    @Update("update user set points = points - #{deductPoints} where id = #{id} and points = #{originalPoints}")
+    Integer subPointsNegative(@Param("id") Long userId, @Param("originalPoints") BigDecimal originalPoints, @Param("deductPoints") BigDecimal deductPoints);
+
+    @Update("update user set balance = balance - #{deduct} where id = #{userId} and balance = #{userBalance} and balance >= #{deduct}")
+    Integer deductBalance(@Param("userId") Long userId, @Param("deduct") BigDecimal deduct, @Param("userBalance") BigDecimal userBalance);
+
+    @Update("update user set balance = balance + #{returnBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer returnBalance(@Param("userId") Long userId, @Param("returnBalance") BigDecimal returnBalance, @Param("userBalance") BigDecimal userBalance);
+
+    @Update("update user set balance = balance + #{addBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer addUserBalance(@Param("userId") Long userId, @Param("userBalance") BigDecimal userBalance, @Param("addBalance") BigDecimal addBalance);
+
+    @Update("update user set balance = balance - #{subBalance} where id = #{userId} and balance = #{userBalance}")
+    Integer deductUserBalance(@Param("userId") Long userId, @Param("userBalance") BigDecimal userBalance, @Param("subBalance") BigDecimal subBalance);
+
+    @Select("select id from user where show_id = #{showId} limit 1")
+    Long getUserIdByShowId(String showId);
+
+    @Select("select cast(ifnull(sum(balance),0)/100 as decimal(11,2)) from user")
+	BigDecimal statisticsRemainBalance();
+
+    @Update("update user set yhs_money = yhs_money + #{addYhsMoney} where id = #{userId} and yhs_money = #{yhsMoney}")
+    Integer addYhsMoney(@Param("userId") Long id, @Param("yhsMoney") BigDecimal yhsMoney, @Param("addYhsMoney") BigDecimal addYhsMoney);
+
+    @Update("update user set yhs_money = yhs_money - #{deductMoney} where id = #{shopUserId} and yhs_money = #{yhsMoney}")
+    Integer subYhShopUserMoney(@Param("shopUserId") Long shopUserId, @Param("yhsMoney") BigDecimal yhsMoney, @Param("deductMoney") BigDecimal deductMoney);
+
+    @Update("update user set yhs_money = yhs_money - #{subMoney} where id = #{shopUserId} and yhs_money >= #{subMoney} and yhs_money > 0")
+    Integer subYhsMoney(@Param("shopUserId") Long shopUserId, @Param("subMoney") BigDecimal subMoney);
+
+	Integer selectUnBindUserNum();
+
+    Integer getLargeNewConsumer(@Param("beginOfDay") DateTime beginOfDay, @Param("largeLimit") String largeLimit);
+
+    @Update("update user set sub_apply_money = sub_apply_money - #{needApplyMoney} where id = #{id} and sub_apply_money >= #{needApplyMoney} and sub_apply_money > 0")
+    Integer suUserSubApplyMoney(@Param("id") Long id, @Param("needApplyMoney") BigDecimal needApplyMoney);
+
+    @Update("update user set sub_apply_money = sub_apply_money + #{addApplyMoney} where id = #{id} and sub_apply_money = #{subApplyMoney}")
+    Integer addDistributeSubMoney(@Param("id") Long id, @Param("subApplyMoney") BigDecimal subApplyMoney, @Param("addApplyMoney") BigDecimal addApplyMoney);
+
+    Integer getNewMaliceUser(DateTime beginOfDay);
+}

+ 11 - 0
luma/src/main/java/com/yhlxj/dao/mapper/midjourney/LumaUserConversationMapper.java

@@ -0,0 +1,11 @@
+package com.yhlxj.dao.mapper.midjourney;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.LumaUserConversation;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 16:03
+ */
+public interface LumaUserConversationMapper extends BaseMapper<LumaUserConversation> {
+}

+ 19 - 0
luma/src/main/java/com/yhlxj/dao/mapper/midjourney/LumaUserMapper.java

@@ -0,0 +1,19 @@
+package com.yhlxj.dao.mapper.midjourney;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yhlxj.dao.model.entity.LumaUser;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ * @author zwhui
+ * @date 2024/4/16 17:46
+ */
+public interface LumaUserMapper extends BaseMapper<LumaUser> {
+
+    @Update("update luma_user set num = num + #{num} where id = #{id}")
+    int incrNum(@Param("id") Long id, @Param("num") Integer num);
+
+    @Update("update luma_user set num = num - #{num} where id = #{id} and num = #{userNum} and num > 0")
+    int decLumaNum(@Param("id") Long id, @Param("num") Integer num, @Param("userNum") Integer userNum);
+}

+ 36 - 0
luma/src/main/java/com/yhlxj/dao/model/BaseEntity.java

@@ -0,0 +1,36 @@
+package com.yhlxj.dao.model;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ * @author chan
+ * @className BaseEntity
+ * @description
+ * @date 2020/7/29 2:28 下午
+ */
+@Data
+@Accessors(chain = true)
+public class BaseEntity {
+
+    @TableId(value = "id",type =IdType.AUTO)
+    private Long id;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+}

+ 35 - 0
luma/src/main/java/com/yhlxj/dao/model/dto/MessageButton.java

@@ -0,0 +1,35 @@
+package com.yhlxj.dao.model.dto;
+
+import lombok.Data;
+
+/**
+ * 可执行按钮
+ */
+@Data
+public class MessageButton {
+    /**
+     * 动作标识
+     */
+    private String customId;
+
+    /**
+     * 图标
+     */
+    private String emoji = "";
+
+    /**
+     * 文本
+     */
+    private String label;
+
+    /**
+     * 类型,系统内部使用
+     */
+    private int type = 2;
+
+    /**
+     * 样式: 2(Primary)、3(Green)
+     */
+    private int style = 2;
+
+}

+ 106 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/Account.java

@@ -0,0 +1,106 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @author chan
+ * @date 2022/9/27 6:17 PM
+ */
+@Getter
+@Setter
+public class Account extends BaseEntity {
+
+    private String title;
+
+    private String account;
+
+    private String password;
+
+    private Boolean isMonth;
+
+    private Long goodsId;
+
+    private String bankCard;
+
+    @TableField(exist = false)
+    private Long skuId;
+
+    private Date startTime;
+
+    private Date expiryTime;
+
+    private BigDecimal amount;
+
+    /**
+     * 国区id
+     */
+    private String cnAccount;
+
+    /**
+     * 美区id
+     */
+    private String usAccount;
+
+    /**
+     * 客服id
+     */
+    private Long customerServiceId;
+
+    /**
+     * chatGpt apiKey
+     */
+    private String apiKey;
+
+    /**
+     * chatGpt 邮箱密码
+     */
+    private String gptEmailPwd;
+
+    /**
+     * 辅助邮箱
+     */
+    private String email;
+
+    private String remark;
+
+    /**
+     * midJourney 登录安全码
+     */
+    private String verifyCodes;
+
+    /**
+     * 密保
+     */
+    private String secret;
+
+    /**
+     * gpt API 密钥
+     */
+    private String apiSecret;
+
+    /**
+     * 账号是否被弃用
+     */
+    private Boolean isAbandon;
+
+    /**
+     * gpt accesToken
+     */
+    private String gptRefreshToken;
+
+    /**
+     * 备用账号类型sku_ids
+     */
+    private String preSkuIds;
+
+    /**
+     * 类型 1通用 2备用
+     */
+    private Integer type;
+}

+ 248 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/GoodsDon.java

@@ -0,0 +1,248 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/9/26 4:58 PM
+ */
+@Getter
+@Setter
+@SearchBean(tables = "goods_don g",
+        where = ":condition:")
+public class GoodsDon extends BaseEntity {
+
+
+    /**
+     * 商品录入人
+     */
+    private String writer;
+
+    /**
+     * 标签
+     */
+    private String tags;
+
+    /**
+     * 商品名称
+     */
+    private String title;
+
+    /**
+     * 缩略图
+     */
+    private String logo;
+
+    /**
+     * 背景图
+     */
+    private String img;
+
+    /**
+     * 视频封面
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private String videoCover;
+
+
+    /**
+     * true 上架 / false 下架
+     */
+    private Boolean status;
+
+    /**
+     * true 存在 / false 删除
+     */
+    private Boolean deleted;
+
+    /**
+     * 详情
+     */
+    private String detail;
+
+    /**
+     * 描述
+     */
+    private String remark;
+
+    /**
+     * 1.虚拟,2.实物,3.潮玩,4.租赁
+     */
+    private Integer type;
+
+    /**
+     * 分类
+     * 1、AI  2、流媒体
+     */
+    private Long category;
+
+    @DbIgnore
+    @TableField(exist = false)
+    private List<Long> categoryList;
+
+    /**
+     * 二级分类
+     * 虚物品 1邀请制 2非邀请制
+     */
+    private Integer secondType;
+
+    private String banner;
+
+    /**
+     * 商品搭配图
+     */
+    private String matchImg;
+
+
+    /**
+     * 是否贴片 false
+     */
+    private Boolean isPatch;
+
+    /**
+     * H5标签
+     */
+    private String mobileTags;
+
+    /**
+     * 平台分销比例
+     */
+    private Integer rate;
+
+    /**
+     * 第二分销比例 不包含套餐
+     */
+    private Integer secondRate;
+
+    /**
+     * 排序
+     */
+    private Integer sortBy;
+
+    /**
+     * 虚拟销量
+     */
+    private Integer virtualSold;
+
+    /**
+     * 最小规格价格
+     */
+    private BigDecimal minPrice;
+
+    /**
+     * 最小规格价格月份
+     */
+    private Integer minMonths;
+
+    /**
+     * 最小规格 天数
+     */
+    private Integer minDays;
+
+    /**
+     * 最大规格价格
+     */
+    private BigDecimal maxPrice;
+
+    /**
+     * 最大规格价格月份
+     */
+    private Integer maxMonths;
+
+    /**
+     * 最大规格 天数
+     */
+    private Integer maxDays;
+
+    /**
+     * 付款说明
+     */
+    private String payDesc;
+
+    /**
+     * 购买说明
+     */
+    private String buyDesc;
+
+    /**
+     * 特殊类型
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private SpecialType specialType;
+
+    /**
+     * 视频
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private String video;
+
+    /**
+     * 推荐标题
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private String recommendTitle;
+
+    @TableField(exist = false)
+    @DbIgnore
+    private List<Long> recommendGoodsList;
+
+    /**
+     * 推荐平台
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private String recommendGoods;
+
+    @TableField(exist = false)
+    @DbIgnore
+    private BigDecimal price;
+
+    @TableField(exist = false)
+    @DbIgnore
+    private Integer soldNum;
+
+    @TableField(exist = false)
+    @DbIgnore
+    private Integer accountCount;
+
+    /**
+     * 是否是特定价格 规格平台
+     */
+    private Boolean isSp;
+
+    /**
+     * 是否有专属用户规格属性
+     */
+    private Boolean isOwns;
+
+    /**
+     * 是否参与分销 默认参与
+     */
+    private Boolean isDistribute;
+
+    /**
+     * 是否开启规格分销 默认未开启
+     */
+    private Boolean isSkuDistribute;
+
+    @AllArgsConstructor
+    @Getter
+    public enum SpecialType {
+        recharge("代充"),
+        courseware("课程"),
+        giftCard("礼品卡")
+        ;
+
+        private String desc;
+
+    }
+}

+ 210 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/GoodsDonSku.java

@@ -0,0 +1,210 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * @author chan
+ * @date 2022/9/26 4:58 PM
+ */
+@Getter
+@Setter
+@SearchBean(tables = "goods_don_sku",
+		where = ":condition:")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class GoodsDonSku extends BaseEntity {
+
+    private Long goodsId;
+
+
+    private String subTitle;
+
+    /**
+     * 商品规格 ids
+     */
+    private String specIds;
+
+    /**
+     * 重复校验规格ids
+     */
+    private String dupSpecIds;
+
+    /**
+     * 商品规格 value
+     */
+    private String specVal;
+
+    /**
+     * 商品规格 json
+     */
+    private String specJson;
+
+    /**
+     * 规格说明信息
+     */
+    private String notifyText;
+
+    /**
+     * H5标签
+     */
+    private String mobileTags;
+
+    /**
+     * 规格背景图
+     */
+    private String img;
+
+    /**
+     * 规格框提示
+     */
+    private String smallMsg;
+
+    /**
+     * 双重验证码次数
+     */
+    private Integer codeNum;
+
+    /**
+     * 商品预览图
+     */
+    private String picture;
+
+    /**
+     * 商品详情图
+     */
+    private String detail;
+
+    /**
+     * 标签
+     */
+    private String tags;
+
+
+    /**
+     * true 上架 / false 下架
+     */
+    private Boolean status;
+
+
+    /**
+     * 车位数
+     */
+    private Integer num;
+
+    /**
+     * 价格
+     */
+    private BigDecimal price;
+
+    /**
+     * 专属用户规格
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private String userOwns;
+
+    /**
+     * 是否开启实物车票时长权益
+     */
+    private Boolean isBenefits;
+
+    /**
+     * 权益列表
+     */
+    private String benefitsList;
+
+    /**
+     * 月数
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Integer months;
+
+    /**
+     * 天数
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Integer days;
+
+//    @DbField("case when spec_val like '%月%' and spec_val not like '%年%' then 0 when spec_val like '%季%' then 1 when spec_val like '%半年%' then 2 when spec_val like '%年%' then 3 else 66 end")
+//    @TableField(exist = false)
+//    private Integer orderBy;
+    /**
+     * 是否是优惠加购规格
+     */
+    private Boolean isDp;
+
+    /**
+     * 是否是镜像服务
+     */
+    private Boolean isMirror;
+
+    /**
+     * 是否是车队形式
+     */
+    private Boolean isCar;
+
+    /**
+     * 限制次数
+     */
+    private Integer gptLimitNum;
+
+    /**
+     * 限制时间
+     */
+    private Long gptLimitTime;
+
+    /**
+     * mj fast次数
+     */
+    private Integer mjFastNum;
+
+    /**
+     * mj relax次数
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Integer mjRelaxNum;
+
+    /**
+     * 特定价格
+     */
+    private BigDecimal specificPrice;
+
+    /**
+     * 特定平台购买用户人群
+     */
+    private String specificGoodsIds;
+
+    /**
+     * 前端展示
+     */
+    @DbIgnore
+    @TableField(exist = false)
+    @JsonProperty("gname")
+    private String gName;
+
+    /**
+     * 前端展示
+     */
+    @DbIgnore
+    @TableField(exist = false)
+    @JsonProperty("sname")
+    private String sName;
+
+    @DbIgnore
+    @TableField(exist = false)
+    private BigDecimal yhShopSkuPrice;
+
+    /**
+     * 排序
+     */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Integer sorted;
+}

+ 138 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/GroupsRelation.java

@@ -0,0 +1,138 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * @author chan
+ * @date 2022/9/26 6:31 PM
+ */
+@Getter
+@Setter
+public class GroupsRelation extends BaseEntity {
+
+    private Long groupsId;
+
+    private Long userId;
+
+    private Date startTime;
+
+    private Date expiryTime;
+
+    private Integer num;
+
+    /**
+     * locking 锁定中
+     * overdue 过期
+     * validity 有效
+     * none 无人
+     */
+    private Status status;
+
+    /**
+     * 账号
+     */
+    private String account;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    private Date submitTime;
+
+    private Date sendTime;
+
+    private String operator;
+
+    private RechargeStatus rechargeStatus;
+
+    /**
+     * 续费意愿处理状态
+     */
+    private Boolean renewStatus;
+
+    /**
+     * 处理状态
+     */
+    private Boolean isHandle;
+
+    /**
+     * 客服
+     */
+    private String customerService;
+
+    /**
+     * midJourney 登录安全码
+     */
+    private String verifyCode;
+
+    private Long yhsId;
+
+    /**
+     * 获取权益时的订单id
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    private Long orderId;
+
+    /**
+     * 更换车票时,新车票id
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    private Long newRelationId;
+
+
+    /**
+     * 车队目前可用停车数
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    private Integer groupsAvailParkingNum;
+
+    /**
+     * 车队最多可用车位数
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    private Integer maxNum;
+
+    /**
+     * 车票获取类型 默认1购买,2.体验
+     */
+    private Integer aqType;
+
+
+    @AllArgsConstructor
+    @Getter
+    public enum Status{
+        locking("锁定中"),
+        overdue("过期"),
+        validity("有效"),
+        none("无人"),
+        outside("额外")
+        ;
+
+        private String desc;
+
+    }
+
+    @AllArgsConstructor
+    @Getter
+    public enum RechargeStatus{
+        waiting("待充值"),
+        complete("已完成"),
+        error("账号错误"),
+        ;
+
+        private String desc;
+
+    }
+}

+ 175 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/GroupsTrips.java

@@ -0,0 +1,175 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * @author chan
+ * @date 2022/9/26 6:31 PM
+ */
+@Getter
+@Setter
+@SearchBean(tables = "groups_trips gt left join goods_don_sku sku on gt.sku_id = sku.id " +
+        "left join goods_don g on g.id = sku.goods_id " +
+        "left join account a on a.id = gt.account_id ")
+public class GroupsTrips {
+
+    @TableId(value = "id",type = IdType.AUTO)
+    @DbField("gt.id")
+    private Long id;
+
+    @DbField("gt.id")
+    @TableField(exist = false)
+    private Long groupId;
+
+    /**
+     * 商品skuId
+     */
+    @DbField("gt.sku_id")
+    private Long skuId;
+
+    /**
+     * 账号id
+     */
+    @DbField("gt.account_id")
+    private Long accountId;
+
+
+    /**
+     * 账号
+     */
+    @DbField("a.account")
+    @TableField(exist = false)
+    private String account;
+
+    /**
+     * 密码
+     */
+    @DbField("a.password")
+    @TableField(exist = false)
+    private String password;
+
+    /**
+     * 商品id
+     */
+    @DbField("g.id")
+    @TableField(exist = false)
+    private Long goodsId;
+
+    /**
+     * 标题
+     */
+    @DbField("g.title")
+    @TableField(exist = false)
+    private String title;
+
+    /**
+     * 商品规格
+     */
+    @DbField("sku.spec_val")
+    @TableField(exist = false)
+    private String specVal;
+    /**
+     * 录入人
+     */
+    @DbField("gt.writer")
+    private Long writer;
+
+    /**
+     * 车位数量
+     */
+    @DbField("gt.num")
+    private Integer num;
+
+    /**
+     * 可用车位数
+     */
+    @DbField("gt.available_num")
+    private Integer availableNum;
+
+    /**
+     * 状态
+     */
+    @DbField("gt.status")
+    private Status status;
+
+    /**
+     * 备注
+     */
+    @DbField("gt.remark")
+    private String remark;
+
+    /**
+     * 开时间
+     */
+    @DbField("gt.start_time")
+    private Date startTime;
+
+    @DbIgnore
+    @TableField(exist = false)
+    private String verifyCodes;
+
+    @DbField("gt.renewal_time")
+    private Date renewalTime;
+
+    @DbField("a.expiry_time")
+    @TableField(exist = false)
+    private Date expiryTime;
+
+    /**
+     * 车队上是否有人
+     */
+    @DbIgnore
+    @TableField(exist = false)
+    private Boolean isExists;
+
+    /**
+     * 可上车队时间
+     */
+    @DbField("gt.parking_time")
+    private Date parkingTime;
+
+    /**
+     * 目前可停车数目
+     */
+    @DbField("gt.available_parking_num")
+    private Integer availableParkingNum;
+
+    /**
+     * 创建时间
+     */
+
+    @DbField("gt.created_time")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    @DbField("gt.update_time")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+
+    @AllArgsConstructor
+    @Getter
+    public enum Status{
+        waiting("等待发车"),
+        validity("已发车"),
+        down("下架"),
+        close("关闭");
+
+        private String desc;
+
+    }
+}

+ 58 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/LumaUser.java

@@ -0,0 +1,58 @@
+package com.yhlxj.dao.model.entity;
+
+/**
+ * @author zwhui
+ * @date 2024/4/16 17:01
+ */
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class LumaUser extends BaseEntity {
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 头像
+     */
+    private String img;
+
+    /**
+     * 用户凭证
+     */
+    private String userToken;
+
+    /**
+     * 过期时间
+     */
+    private Date expireTime;
+
+    /**
+     * 车票id
+     */
+    private Long relationId;
+
+    /**
+     * 可用次数
+     */
+    private Integer num;
+
+    /**
+     * 车票类型 2试用
+     */
+    @DbIgnore
+    @TableField(exist = false)
+    private Integer aqType;
+    /**
+     * 拉黑
+     */
+    private Boolean isBlack;
+}

+ 94 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/LumaUserConversation.java

@@ -0,0 +1,94 @@
+package com.yhlxj.dao.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 11:10
+ */
+@Data
+@SearchBean(tables = "midjourney_user_conversation")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class LumaUserConversation extends BaseEntity implements Serializable {
+
+    private Long userId;
+
+
+    /**
+     * 任务id
+     */
+    private String taskId;
+
+    /**
+     * 开始时间
+     */
+    private Long startTime;
+
+    /**
+     * 完成时间
+     */
+    private Long finishTime;
+
+    /**
+     * 描述
+     */
+    private String prompt;
+
+    /**
+     * 任务状态,可用值:pending
+     */
+    private String status;
+
+    /**
+     * 失败原因
+     */
+    private String failReason;
+
+    /**
+     * 进度
+     */
+    private String progress;
+
+    /**
+     * 视频地址
+     */
+    private String videoUrl;
+
+    /**
+     * 缩量图
+     */
+    private String thumbnail;
+
+    /**
+     * 视频时间(5s)
+     */
+    private Integer time;
+
+
+    @DbIgnore
+    @TableField(exist = false)
+    private Integer progressNum = -1;
+
+    public LumaUserConversation setProgress(String progress) {
+        this.progress = progress;
+        try {
+            if(StringUtils.isNotBlank(progress)){
+                this.progressNum = Integer.parseInt(this.progress.replace("%",""));
+            }
+            if ("FAILURE".equals(this.getStatus())){
+                this.progressNum = 100;
+            }
+        }catch (Exception e) {
+
+        }
+        return this;
+    }
+}

+ 128 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/User.java

@@ -0,0 +1,128 @@
+package com.yhlxj.dao.model.entity;
+
+/**
+ * @author chan
+ * @date 2024/6/24 17:59
+ */
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @author chan
+ * @date 2021/10/10 下午2:53
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+public class User extends BaseEntity implements Serializable {
+    /** 手机号;手机号 */
+    private String phone ;
+    /** 手机号登录 */
+    private String loginPhone;
+    /**
+     * 邮箱登录
+     */
+    private String email;
+    /** 昵称;昵称 */
+    private String nickname ;
+    /** 头像;头像 */
+    private String headimgurl ;
+
+    private String showId;
+
+    /** 开放平台唯一id;开放平台唯一id */
+    private String unionid ;
+
+    /**
+     * openid
+     */
+    private String openId;
+
+    /** 省份;省份 */
+    private String province ;
+    /** 国家;国家 */
+    private String country ;
+
+    /**
+     * 城市
+     */
+    private String city ;
+
+    /** 性别;性别 */
+    private Integer sex ;
+
+    private BigDecimal points;
+
+    private BigDecimal activePoints;
+
+    /**
+     * 余额 分
+     */
+    private BigDecimal balance;
+
+    /**
+     * 用户店铺提成金额
+     */
+    private BigDecimal yhsMoney;
+
+    /**
+     * 用户下级渠道提成金额
+     */
+    private BigDecimal subApplyMoney;
+
+    /**
+     * 推广id
+     */
+    private Long popularizeId;
+
+    /**
+     * 用户是否被拉黑 true是
+     */
+    private Boolean isBlack;
+
+    /**
+     * 注册环境
+     * h5:移动端浏览器登录
+     * 微信:公众号h5授权登录
+     * pc:浏览器登录,包括微信扫码在浏览器登录
+     */
+    private String registerEnv;
+
+    /**
+     * 最后登录时间
+     */
+    private Date lastTime;
+
+    @TableField(exist = false)
+    private Long emailUserId;
+
+    @TableField(exist = false)
+    private Long phoneUserId;
+
+    /**
+     * 是否是谷歌授权登录
+     */
+    private Boolean isGoogle;
+
+    @Getter
+    public enum RegisterEnv{
+        wx("公众号h5授权登录"),
+        h5("移动端浏览器登录"),
+        pc("浏览器登录,包括微信扫码在浏览器登录"),
+        ;
+        String desc;
+
+        RegisterEnv(String desc) {
+            this.desc = desc;
+        }
+    }
+}
+

+ 25 - 0
luma/src/main/java/com/yhlxj/dao/model/entity/UserBindDetail.java

@@ -0,0 +1,25 @@
+package com.yhlxj.dao.model.entity;
+
+import com.yhlxj.dao.model.BaseEntity;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: UserBindDetail
+ *创建者: JavaZou
+ *创建时间:2022/12/27 13:39
+ */
+@Getter
+@Setter
+public class UserBindDetail extends BaseEntity {
+	private Long userId;
+
+	private String phone;
+
+	private Long phoneUserId;
+
+	private String email;
+
+	private Long emailUserId;
+}

+ 55 - 0
luma/src/main/java/com/yhlxj/dao/model/enums/WsMessageTypeEnum.java

@@ -0,0 +1,55 @@
+package com.yhlxj.dao.model.enums;
+
+import lombok.Getter;
+
+import java.util.stream.Stream;
+
+/**
+ * @author chan
+ * @date 2024/6/27 18:43
+ */
+@Getter
+public enum WsMessageTypeEnum {
+
+
+    PING("ping"), PONG("pong"), MESSAGE("msg"),
+
+    //客服端反馈子类型
+    CLIENT_TASK_ID("task_id"),//开始连接
+    CLIENT_CLOSE("close"),//关闭连接
+    CLIENT_ACK("ack"),//收到
+    CLIENT_RESURGENCE("resurgence"),//客服端复活
+
+    //服务端发送子类型
+    SERVER_TASK_INFO("task_info"),//服务端发送问题
+    SERVER_TASK_NOT_FIND("task_not_find"),
+    SERVER_ERROR("error"),//错误信息
+    SERVER_FAIL("fail"),//错误信息
+    ;
+
+    public final String type;
+
+    WsMessageTypeEnum(String type) {
+        this.type = type;
+    }
+
+    /**
+     * @author chan 验证枚举
+     * @return boolean
+     **/
+    public static boolean valid(String type) {
+        return Stream.of(WsMessageTypeEnum.values()).anyMatch(val -> val.getType().equals(type));
+    }
+
+    /**
+     *
+     * @author chan
+     * @param type
+     * @return
+     */
+    @SuppressWarnings("rawtypes")
+    public static WsMessageTypeEnum getMessageType(String type){
+        return Stream.of(WsMessageTypeEnum.values())
+                .filter(enums-> enums.getType().equals(type)).findFirst().get();
+    }
+}

+ 34 - 0
luma/src/main/java/com/yhlxj/dao/model/response/SubmitResult.java

@@ -0,0 +1,34 @@
+package com.yhlxj.dao.model.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class SubmitResult {
+
+    /**
+     * 记录id
+     */
+    private Long rid;
+
+    private String id;
+
+    private String prompt;
+
+    private String state;
+
+    @JsonProperty("created_at")
+    private String createdAt;
+
+    private Video video;
+
+
+    @Data
+    public static class Video {
+        private String url;
+        private int width;
+        private int height;
+        private String thumbnail;
+        private String download_url;
+    }
+}

+ 65 - 0
luma/src/main/java/com/yhlxj/dao/model/response/VideoTask.java

@@ -0,0 +1,65 @@
+package com.yhlxj.dao.model.response;
+
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class VideoTask {
+    private int id;
+    private long created_at;
+    private long updated_at;
+    private String task_id;
+    private String platform;
+    private int user_id;
+    private int channel_id;
+    private int quota;
+    private String notify_hook;
+    private int token_id;
+    private int agent_id;
+    private String action;
+    private String status;
+    private String fail_reason;
+    private long submit_time;
+    private long start_time;
+    private long finish_time;
+    private String progress;
+    private Properties properties;
+    private String search_item;
+    private Data data;
+
+    // Getters and setters
+
+    @Getter
+    @Setter
+    public static class Properties {
+        private String input;
+
+        // Getters and setters
+    }
+
+    @Getter
+    @Setter
+    public static class Data {
+        private String id;
+        private Boolean liked;
+        private String state;
+        private Video video;
+        private String prompt;
+        private String created_at;
+        private Integer estimate_wait_seconds;
+
+        // Getters and setters
+    }
+
+    @Getter
+    @Setter
+    public static class Video {
+        private String url;
+        private int width;
+        private int height;
+        private String thumbnail;
+        private String download_url;
+    }
+}

+ 769 - 0
luma/src/main/java/com/yhlxj/redis/RedisService.java

@@ -0,0 +1,769 @@
+package com.yhlxj.redis;
+
+import com.cyksj.common.util.StringUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ZSetOperations;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * @description 定义常用的 Redis操作
+ * @author chan
+ * @date 2021-03-31 5:34 下午
+ */
+@Service
+@SuppressWarnings("all")
+public class RedisService {
+
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+
+    public static String env = "dev";
+
+    @Value("${spring.profiles.active}")
+    private String active;
+
+    @PostConstruct
+    public void init(){
+        env = active + ":";
+    }
+
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     * @return Boolean
+     */
+    public Boolean expire(String key, Long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key获取过期时间
+     *
+     * @param key 键 不能为 null
+     * @return 时间(秒) 返回 0代表为永久有效
+     */
+    public Long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断 key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public Boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(Arrays.asList(key));
+            }
+        }
+    }
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * String缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public String getStr(String key) {
+            return key == null ? null : StringUtil.getString(redisTemplate.opsForValue().get(key));
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public Boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public Boolean set(String key, Object value, Long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     *
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     * @return Long
+     */
+    public Long incr(String key, Long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     *
+     * @param key   键
+     * @param delta 要减少几
+     * @return Long
+     */
+    public Long decr(String key, Long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为 null
+     * @param item 项 不能为 null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取 hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public Boolean hmset(String key, Map<Object, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public Boolean hmset(String key, Map<Object, Object> map, Long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public Boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public Boolean hset(String key, String item, Object value, Long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为 null
+     * @param item 项 可以使多个不能为 null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为 null
+     * @param item 项 不能为 null
+     * @return true 存在 false不存在
+     */
+    public Boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return Double
+     */
+    public Double hincr(String key, String item, Double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return Double
+     */
+    public Double hdecr(String key, String item, Double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    /**
+     * 根据 key获取 Set中的所有值
+     *
+     * @param key 键
+     * @return Set
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public Boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public Long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public Long sSetAndTime(String key, Long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return Long
+     */
+    public Long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public Long setRemove(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().remove(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束 0 到 -1代表所有值
+     * @return List
+     */
+    public List<Object> lGet(String key, Long start, Long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return Long
+     */
+    public Long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;
+     *              index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return Object
+     */
+    public Object lGetIndex(String key, Long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return Boolean
+     */
+    public Boolean lSet(String key, Object value, Long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return Boolean
+     */
+    public Boolean lSet(String key, List<Object> value, Long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lUpdateIndex(String key, Long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public Long lRemove(String key, Long count, Object value) {
+        try {
+            return redisTemplate.opsForList().remove(key, count, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    public boolean setNx(String key, Object value, Long time) {
+        if (time > 0) {
+            return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
+        }
+        return false;
+    }
+
+
+    /**
+     * 添加元素到ZSet
+     */
+    public Boolean zAdd(String key, double score, Object value) {
+        try {
+            return redisTemplate.opsForZSet().add(key, value, score);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    // 增加ZSet元素的分数
+    public Double zIncrementScore(String key, String value, double delta) {
+        try {
+            return redisTemplate.opsForZSet().incrementScore(key, value, delta);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    // 封装获取ZSet分数操作
+    public Double zScore(String key, Object value) {
+        try {
+            return redisTemplate.opsForZSet().score(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    // 封装ZSet添加或更新分数操作
+    public Boolean zAddOrUpdateScore(String key, Object value, double score) {
+        try {
+            return redisTemplate.opsForZSet().add(key, value, score);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 获取ZSet的大小
+     */
+    public Long zCard(String key) {
+        try {
+            return redisTemplate.opsForZSet().zCard(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    /**
+     * 获取ZSet的大小
+     */
+    public Long zCount(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForZSet().count(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    // 封装获取ZSet范围内成员的方法
+    public Set<Object> zRange(String key, long start, long end) {
+        try {
+            ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
+            return zSetOperations.range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+    /**
+     * 删除ZSet中分数范围内的元素
+     */
+    public Long zRemoveRangeByScore(String key, double min, double max) {
+        try {
+            return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+    /**
+     * 返回有序集 key 中,所有分数介于 min 和 max 之间(包括等于 min 或 max)的成员。
+     * 有序集成员按分数值递增(从小到大)次序排列。
+     * 此方法还支持限制返回的元素数量。
+     *
+     * @param key 有序集合的key
+     * @param min 分数范围的最小值
+     * @param max 分数范围的最大值
+     * @param offset 开始返回的索引位置
+     * @param count 返回的最大元素数量
+     * @return 介于最小值和最大值之间的成员的集合
+     */
+    public Set<Object> zRangeByScore(String key, double min, double max, long offset, long count) {
+        return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
+    }
+
+    /**
+     * 从ZSet获取所有成员及其分数
+     * @param key ZSet的键
+     * @return ZSet中所有成员及其分数的集合
+     */
+    public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, long start, long end){
+        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+    }
+
+    /**
+     * 检查指定值是否存在于有序集合中
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean checkValueExistsInZSet(String key, String value) {
+        // 检查指定值是否存在于有序集合中
+        return redisTemplate.opsForZSet().score(key, value) != null;
+    }
+
+    /**
+     * 从ZSet中删除指定的成员
+     *
+     * @param key ZSet的键
+     * @param value 要删除的成员值
+     * @return 被成功移除的成员数量
+     */
+    public Long zRemove(String key, Object value) {
+        try {
+            return redisTemplate.opsForZSet().remove(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+
+
+
+
+    public Set<String> keys(String name) {
+        return redisTemplate.keys(name);
+    }
+
+    @AllArgsConstructor
+    @Getter
+    public enum key {
+        LUMA_NUM_LIMIT("luma_num_limit:", "luma次数", 60 * 60 * 48L),
+        LUMA_CONVERSATION("luma_conversation:", "midjourney conversation ", 60 * 60 * 48L),
+        LUMA_PROGRESS("luma:task:progress:","task progress 任务进度", 60 * 60 * 1L),
+        LUMA_QUERY("luma:query:", "luma query", 60 * 60 * 2L),
+        ;
+
+        private String name;
+
+        private String desc;
+
+        private long timeout;
+
+        public String getEnvName() {
+            return this.name + env;
+        }
+        public String getNameFormat(Object ... str) {
+            return String.format(this.getEnvName(), str);
+        }
+
+    }
+}

+ 38 - 0
luma/src/main/java/com/yhlxj/service/common/EnvCommonService.java

@@ -0,0 +1,38 @@
+package com.yhlxj.service.common;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/*
+ *项目名: netflix
+ *文件名: EnvCommon
+ *创建者: JavaZou
+ *创建时间:2022/10/26 17:10
+ */
+@Component
+@Getter
+@Setter
+public class EnvCommonService {
+
+	public static final String active = "dev";
+
+	public static final String active_prd = "prd";
+
+	public static final String active_pre = "pre";
+
+	public static String DISTRIBUTE_DEFAULT_URL_PREFIX;
+
+	@Value(value = "${spring.profiles.active}")
+	private String env;
+
+	public Boolean isPrdEnv() {
+		return EnvCommonService.active_prd.equals(getEnv());
+	}
+
+	public Boolean isPreEnv() {
+		return EnvCommonService.active_pre.equals(getEnv());
+	}
+
+}

+ 37 - 0
luma/src/main/java/com/yhlxj/service/midjourney/LumaService.java

@@ -0,0 +1,37 @@
+package com.yhlxj.service.midjourney;
+
+
+import com.cyksj.model.dto.SubmitVideoDTO;
+import com.yhlxj.dao.model.entity.LumaUser;
+import com.yhlxj.dao.model.entity.LumaUserConversation;
+import com.yhlxj.dao.model.response.SubmitResult;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 15:44
+ */
+public interface LumaService {
+    LumaUser getUser(String userToken);
+
+    /**
+     * 提交video任务
+     */
+    SubmitResult submitVideo(LumaUser user, SubmitVideoDTO videoDTO, Long num) throws Exception;
+
+    SubmitResult getVideoTaskInfoByTaskId(LumaUser user, Long rid) throws Exception;
+
+    void submitVideoNotify(String json);
+
+    SubmitResult extendedVideo(LumaUser user, SubmitVideoDTO videoDTO) throws Exception;
+
+    LumaUserConversation getConversationById(String taskId);
+
+    List<LumaUserConversation> conversationListByIds(List<Long> ids, LumaUser user);
+
+    LumaUserConversation getVideoConversationByTid(LumaUser user, Long rid);
+
+    String updateCover(Long id, Long userId) throws IOException;
+}

+ 475 - 0
luma/src/main/java/com/yhlxj/service/midjourney/impl/LumaServiceImpl.java

@@ -0,0 +1,475 @@
+package com.yhlxj.service.midjourney.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.model.dto.SubmitVideoDTO;
+import com.yhlxj.dao.mapper.GroupsRelationMapper;
+import com.yhlxj.dao.mapper.midjourney.LumaUserConversationMapper;
+import com.yhlxj.dao.mapper.midjourney.LumaUserMapper;
+import com.yhlxj.dao.model.entity.GroupsRelation;
+import com.yhlxj.dao.model.entity.LumaUser;
+import com.yhlxj.dao.model.entity.LumaUserConversation;
+import com.yhlxj.dao.model.response.SubmitResult;
+import com.yhlxj.dao.model.response.VideoTask;
+import com.yhlxj.redis.RedisService;
+import com.yhlxj.service.common.EnvCommonService;
+import com.yhlxj.service.midjourney.LumaService;
+import com.yhlxj.util.COSUtil;
+import com.yhlxj.util.VideoCoverExtractor;
+import com.yhlxj.web.wss.WssSession;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 15:44
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class LumaServiceImpl implements LumaService {
+    private static final String LUMA_BASE_URL = "https://api.bltcy.ai";
+    private static final String AUTH = "Bearer sk-YTg6N3QH6PLTFLbM9291B1Ae12754bC4B26225F1B95f442c";
+
+    private final RedisService redisService;
+
+    private final LumaUserMapper lumaUserMapper;
+
+    @Value("${luma.url}")
+    private String lumaHost;
+
+    private final EnvCommonService envCommonService;
+
+    private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+    private final LumaUserConversationMapper conversationMapper;
+
+    private final GroupsRelationMapper groupsRelationMapper;
+
+    private static final String VIDEO_NOTIFY_URL = "/api/applets/luma/video/notify";
+
+    //webSocket
+    public static Map<Long, WssSession> WSS_SESSION_MAP = new ConcurrentHashMap<>();
+
+    private static final List<String> progress = List.of("100%");
+
+    private static final List<String> status = List.of("pending", "processing");
+
+    private static final List<String> FINISH_STATUS = List.of("completed", "failed");
+
+    @Override
+    public LumaUser getUser(String userToken) {
+        if(StringUtils.isBlank(userToken)){
+            throw BusinessRuntimeException.getInstance("10001","请重新至官网登录");
+        }
+        LumaUser lumaUser = lumaUserMapper.selectOne(Wrappers.lambdaQuery(LumaUser.class)
+                .eq(LumaUser::getUserToken, userToken).last(" limit 1"));
+        if (lumaUser == null){
+            throw BusinessRuntimeException.getInstance("10001","会话已过期,请重新登录");
+        }
+        if(!redisService.hasKey(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId())){
+            redisService.set(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId(), lumaUser.getNum());
+        }
+
+        Long relationId = lumaUser.getRelationId();
+        GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+        lumaUser.setAqType(relation.getAqType());
+        return lumaUser;
+    }
+
+    public LumaUserConversation saveConversation(Long userId,SubmitResult result,String prompt) throws Exception {
+        String taskId = result.getId();
+        LumaUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getTaskId, result.getId())
+                .last("limit 1"));
+        if (conversation == null){
+            conversation = new LumaUserConversation();
+            conversation.setUserId(userId);
+            conversation.setTaskId(taskId);
+            conversation.setPrompt(prompt);
+            conversation.setStartTime(System.currentTimeMillis());
+            conversation.setProgress("0%");
+            conversation.setStatus("pending");
+            conversation.setTaskId(taskId);
+        }
+        conversation.setTime(Optional.ofNullable(conversation.getTime()).orElse(0) + 5);
+        if (conversation.getId() == null) {
+            conversationMapper.insert(conversation);
+        }else {
+            conversationMapper.updateById(conversation);
+        }
+        return conversation;
+    }
+
+    public SubmitResult submit(LumaUser user, Map<String, Object> param, Long num) throws Exception {
+        String url = LUMA_BASE_URL + "/luma/generations";
+        HttpRequest request = HttpRequest.post(url).body(Jsons.toJson(param));
+        request.header("Authorization", AUTH);
+
+        String body = request.setConnectionTimeout(10000).execute().body();
+        log.info("return body:{}", body);
+        SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
+        if (submitResult.getId() == null) {
+            throw BusinessRuntimeException.getInstance("请输入正确的生成视频信息");
+        }
+        return submitResult;
+    }
+
+    @Override
+    public SubmitResult submitVideo(LumaUser user,SubmitVideoDTO videoDTO, Long num) throws Exception {
+        Map<String, Object> videoParam = MapUtil.builder(new HashMap<String, Object>())
+                .put("aspect_ratio", "16:9")
+                .build();
+        String prompt = videoDTO.getPrompt();
+        Boolean expandPrompt = videoDTO.getExpandPrompt();
+        String picture = videoDTO.getPicture();
+        String imageEndUrl = videoDTO.getImageEndUrl();
+        if (StrUtil.isNotBlank(prompt)) {
+            videoParam.put("user_prompt", prompt);
+        }
+        if (expandPrompt != null) {
+            videoParam.put("expand_prompt", expandPrompt);
+        }
+        if (StrUtil.isNotBlank(picture)) {
+            videoParam.put("image_url", picture);
+        }
+        if (StrUtil.isNotBlank(imageEndUrl)) {
+            videoParam.put("image_end_url", imageEndUrl);
+        }
+        videoParam.put("notify_hook", lumaHost + (EnvCommonService.active_pre.equals(envCommonService.getEnv()) ? "/8083" : "/8084") + VIDEO_NOTIFY_URL);
+        SubmitResult result = submit(user, videoParam, num);
+        LumaUserConversation lumaUserConversation = saveConversation(user.getId(), result, prompt);
+        result.setRid(lumaUserConversation.getId());
+        return result;
+    }
+
+
+    @Override
+    public SubmitResult getVideoTaskInfoByTaskId(LumaUser user, Long rid) throws Exception {
+        LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getUserId, user.getId())
+                .eq(LumaUserConversation::getId, rid));
+        if (lumaUserConversation == null) {
+            throw BusinessRuntimeException.getInstance("视频不存在");
+        }
+        String url = LUMA_BASE_URL + "/luma/generations/" + lumaUserConversation.getTaskId();
+        HttpRequest request = HttpRequest.get(url);
+        request.header("Authorization", AUTH);
+
+        String body = request.setConnectionTimeout(50000).execute().body();
+        log.info("action body:{}", body);
+        SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
+        if (submitResult.getId() == null) {
+            throw BusinessRuntimeException.getInstance("视频不存在");
+        }
+        if (FINISH_STATUS.contains(submitResult.getState())) {
+            String id = submitResult.getId();
+            if (!FINISH_STATUS.contains(lumaUserConversation.getStatus())) {
+                lumaUserConversation.setStatus(submitResult.getState());
+                SubmitResult.Video video = submitResult.getVideo();
+                lumaUserConversation.setFinishTime(System.currentTimeMillis());
+                lumaUserConversation.setVideoUrl(video.getUrl());
+                lumaUserConversation.setThumbnail(video.getThumbnail());
+                TASK_EXECUTOR.execute(() -> {
+                    //同步
+                    sync(lumaUserConversation);
+                });
+            }
+        }
+        return submitResult;
+    }
+
+    @Override
+    public void submitVideoNotify(String json) {
+        log.info("notifyHook:{}", json);
+        if (StrUtil.isEmpty(json)) {
+            return;
+        }
+        JSONObject jsons = JSONUtil.parseObj(json);
+        VideoTask videoTask = JSONUtil.toBean(jsons, VideoTask.class);
+        VideoTask.Data videoTaskData = videoTask.getData();
+        String taskId = videoTaskData.getId();
+        LumaUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getTaskId, taskId)
+                .last("limit 1"));
+        if (conversation == null) {
+            log.info("回调任务taskId:{}不存在", taskId);
+            return;
+        }
+        conversation.setStatus(videoTaskData.getState());
+        VideoTask.Video video = videoTaskData.getVideo();
+        if (video != null) {
+            conversation.setVideoUrl(video.getUrl());
+            conversation.setThumbnail(video.getThumbnail());
+        }
+        conversation.setFailReason(videoTask.getFail_reason());
+        if (videoTask.getFinish_time() != 0) {
+            conversation.setFinishTime(videoTask.getFinish_time());
+        }
+        redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + conversation.getTaskId(), conversation,RedisService.key.LUMA_CONVERSATION.getTimeout());
+
+        TASK_EXECUTOR.execute(() -> {
+            try {
+                LumaUserConversation conversation1 = new LumaUserConversation();
+                BeanUtil.copyProperties(conversation, conversation1);
+                sync(conversation1);
+            } catch (Exception e) {
+                log.error("同步任务出错 taskId:{}, error:{}", conversation.getTaskId(), StringUtil.getErrorText(e));
+            }
+        });
+    }
+
+    @Override
+    public SubmitResult extendedVideo(LumaUser user, SubmitVideoDTO videoDTO) throws Exception {
+        String taskId = videoDTO.getTaskId();
+        String prompt = videoDTO.getPrompt();
+        Boolean expandPrompt = videoDTO.getExpandPrompt();
+        String url = LUMA_BASE_URL + String.format("/luma/generations/%s/extend", taskId);
+
+        LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getTaskId, taskId)
+                .eq(LumaUserConversation::getUserId, user.getId())
+                .last("limit 1"));
+        if (lumaUserConversation == null) {
+            throw BusinessRuntimeException.getInstance("视频不存在");
+        }
+        if (!StrUtil.equals("completed", lumaUserConversation.getStatus())) {
+            throw BusinessRuntimeException.getInstance("视频未生成");
+        }
+        Map<String, Object> videoParam = MapUtil.builder(new HashMap<String, Object>())
+                .put("aspect_ratio", "16:9")
+                .build();
+        videoParam.put("user_prompt", prompt);
+        if (expandPrompt != null) {
+            videoParam.put("expand_prompt", expandPrompt);
+        }
+        String picture = videoDTO.getPicture();
+        String imageEndUrl = videoDTO.getImageEndUrl();
+        if (StrUtil.isNotBlank(picture)) {
+            videoParam.put("image_url", picture);
+        }
+        if (StrUtil.isNotBlank(imageEndUrl)) {
+            videoParam.put("image_end_url", imageEndUrl);
+        }
+        //线上接口8084
+        videoParam.put("notify_hook", lumaHost + (EnvCommonService.active_pre.equals(envCommonService.getEnv()) ? "/8083" : "/8084") + VIDEO_NOTIFY_URL);
+
+        HttpRequest request = HttpRequest.post(url).setConnectionTimeout(10000).body(Jsons.toJson(videoParam));
+        request.header("Authorization", AUTH);
+        String body = request.execute().body();
+        SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
+        log.info("return body: {}", body);
+        if (submitResult.getId() == null) {
+            throw BusinessRuntimeException.getInstance("请输入正确的描述");
+        }
+        //重新设置taskId
+        lumaUserConversation.setTaskId(submitResult.getId());
+        lumaUserConversation.setStatus(submitResult.getState());
+        lumaUserConversation.setPrompt(submitResult.getPrompt());
+        conversationMapper.updateById(lumaUserConversation);
+        return submitResult;
+    }
+
+    @Override
+    public LumaUserConversation getConversationById(String taskId) {
+        //从缓存中查询 该任务是否存在
+        LumaUserConversation conversation;
+        Object obj = redisService.get(RedisService.key.LUMA_CONVERSATION.getName() + taskId);
+        if (obj == null) {
+            conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                    .eq(LumaUserConversation::getTaskId, taskId)
+                    .last("limit 1"));
+            redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + taskId, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
+        } else {
+            try {
+                conversation = (LumaUserConversation) obj;
+            } catch (Exception e) {
+                String jsonStr = JSONUtil.toJsonStr(obj);
+                conversation = JSONUtil.parseObj(jsonStr).toBean(LumaUserConversation.class);
+                redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + taskId, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
+            }
+        }
+        return conversation;
+    }
+
+
+    @Override
+    public List<LumaUserConversation> conversationListByIds(List<Long> ids, LumaUser user) {
+        List<LumaUserConversation> list = new ArrayList<>();
+        Integer selectCount = conversationMapper.selectCount(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getUserId, user.getId())
+                .in(LumaUserConversation::getTaskId, ids));
+        if (selectCount != ids.size()) {
+            throw BusinessRuntimeException.getInstance("视频不存在");
+        }
+        ids.forEach(id -> {
+            String queryKey = RedisService.key.LUMA_QUERY.getName();
+            Long count = redisService.incr(queryKey + id, 1L);
+            if (count % 5 == 0) {
+                try {
+                    SubmitResult submitResult = getVideoTaskInfoByTaskId(user, id);
+                    LumaUserConversation conversation = new LumaUserConversation();
+                    SubmitResult.Video video = submitResult.getVideo();
+                    conversation.setStatus(submitResult.getState());
+                    conversation.setVideoUrl(video.getUrl());
+                    if (!FINISH_STATUS.contains(conversation.getStatus())) {
+                        conversation.setFinishTime(System.currentTimeMillis());
+                    }
+                    list.add(conversation);
+                } catch (Exception e) {
+
+                }
+            } else {
+                Object obj = redisService.get(RedisService.key.LUMA_CONVERSATION.getName() + id);
+                LumaUserConversation conversation;
+                try {
+                    conversation = (LumaUserConversation) obj;
+                } catch (Exception e) {
+                    String jsonStr = JSONUtil.toJsonStr(obj);
+                    conversation = JSONUtil.parseObj(jsonStr).toBean(LumaUserConversation.class);
+                    redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + id, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
+                }
+                list.add(conversation);
+            }
+        });
+        return list;
+    }
+
+
+    @Override
+    public LumaUserConversation getVideoConversationByTid(LumaUser user, Long rid) {
+        if (rid == null) {
+            return null;
+        }
+        LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getUserId, user.getId())
+                .in(LumaUserConversation::getId, rid)
+                .select(LumaUserConversation::getId, LumaUserConversation::getTaskId, LumaUserConversation::getPrompt, LumaUserConversation::getVideoUrl, LumaUserConversation::getStartTime, LumaUserConversation::getFinishTime, LumaUserConversation::getTime, LumaUserConversation::getStatus, LumaUserConversation::getThumbnail));
+        return lumaUserConversation;
+    }
+
+    @Override
+    public String updateCover(Long id, Long userId) throws IOException {
+        LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
+                .eq(LumaUserConversation::getId, id)
+                .eq(LumaUserConversation::getUserId, userId));
+        if (lumaUserConversation == null) {
+            return null;
+        }
+        if (FINISH_STATUS.contains(lumaUserConversation.getStatus()) && StrUtil.isNotBlank(lumaUserConversation.getVideoUrl())) {
+            String coverFrameByUrl = VideoCoverExtractor.extractVideoCoverFrameByUrl(lumaUserConversation.getVideoUrl());
+            if (StrUtil.isNotBlank(coverFrameByUrl)) {
+                conversationMapper.update(null, Wrappers.lambdaUpdate(LumaUserConversation.class)
+                        .set(LumaUserConversation::getThumbnail, coverFrameByUrl)
+                        .eq(LumaUserConversation::getId, lumaUserConversation.getId()));
+                return coverFrameByUrl;
+            }
+        }
+        return null;
+    }
+
+    public void sync(LumaUserConversation conversation) {
+        if (FINISH_STATUS.contains(conversation.getStatus())) {
+            //重复更新数据
+            if (!redisService.setNx(conversation.getId().toString(), conversation.getId(), 10l)) {
+                return;
+            }
+            log.info("同步任务:{},进度:{}",conversation.getTaskId(),conversation.getProgress());
+            Long userId = conversation.getUserId();
+            LumaUserConversation dbConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class).eq(LumaUserConversation::getTaskId, conversation.getTaskId())
+                    .eq(LumaUserConversation::getUserId, userId).orderByDesc(LumaUserConversation::getId).last("limit 1"));
+            if (dbConversation != null) {
+                if ("completed".equals(dbConversation.getStatus())) {
+                    return;
+                }
+                conversation.setProgress("100%");
+                conversation.setId(dbConversation.getId());
+                conversation.setTime(Optional.ofNullable(dbConversation.getTime()).orElse(0) + 5);
+                conversationMapper.updateById(conversation);
+                TASK_EXECUTOR.execute(() -> {
+                    try {
+                        String videoUrl = conversation.getVideoUrl();
+                        if (StringUtil.isNotBlank(videoUrl)) {
+                            //上传视频至腾讯云
+                            String f_videoUrl = uploadVideoUrl(videoUrl, "conversation" + dbConversation.getId(), conversation);
+                            conversation.setVideoUrl(f_videoUrl);
+                            conversationMapper.updateById(conversation);
+                        }
+                    } catch (IOException e) {
+                        log.error("上传视频失败",e);
+                    }
+                });
+                //失败返还次数
+                if ("failed".equals(conversation.getStatus())){
+                    LambdaUpdateWrapper<LumaUser> wrapper = Wrappers.lambdaUpdate(LumaUser.class)
+                            .eq(LumaUser::getId, userId);
+                    Object num = redisService.incr(RedisService.key.LUMA_NUM_LIMIT.getName() + userId, 1L);
+                    wrapper.set(LumaUser::getNum, num);
+                    lumaUserMapper.update(null, wrapper);
+                }
+            }
+        }
+    }
+
+
+    private static String uploadVideoUrl(String url, String prefix, LumaUserConversation conversation) throws IOException {
+        byte[] body;
+        try {
+            body = HttpUtil.downloadBytes(url);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+
+        // 创建临时文件并将图像字节写入文件
+        Path tempFile = Files.createTempFile(prefix, ".mp4");
+        try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
+            fos.write(body);
+        }
+
+        //抓取视频封面
+        conversation.setThumbnail(VideoCoverExtractor.extractVideoCoverFrame(tempFile));
+
+        String result = url;
+
+        //上传视频并处理潜在的异常
+        try (InputStream inputStream = Files.newInputStream(tempFile)) {
+            String contentType = Files.probeContentType(tempFile);
+            String originalFilename = tempFile.getFileName().toString();
+            String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
+            result = COSUtil.upLoadVideo(inputStream, ext, contentType);
+        } catch (Exception e) {
+            log.error("上传视频异常:{}", StringUtil.getErrorText(e));
+        } finally {
+            try {
+                Files.deleteIfExists(tempFile);
+            } catch (IOException e) {
+                log.error("删除临时文件异常:{}", StringUtil.getErrorText(e));
+            }
+        }
+
+        log.info("上传视频结果:url:{},json:{}", url, result);
+        return result;
+    }
+}

+ 19 - 0
luma/src/main/java/com/yhlxj/service/user/UserBindRelationService.java

@@ -0,0 +1,19 @@
+package com.yhlxj.service.user;
+
+
+import com.yhlxj.dao.model.entity.User;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: UserBindRelationService
+ *创建者: JavaZou
+ *创建时间:2023/8/1 15:03
+ */
+public interface UserBindRelationService {
+	/**
+	 * 获取关联用户id集合
+	 */
+	List<Long> getRelationUserIdList(Long userId, User selectUser);
+}

+ 87 - 0
luma/src/main/java/com/yhlxj/service/user/impl/UserBindRelationServiceImpl.java

@@ -0,0 +1,87 @@
+package com.yhlxj.service.user.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.enums.GatewayApiCode;
+import com.yhlxj.dao.mapper.UserBindDetailMapper;
+import com.yhlxj.dao.mapper.UserMapper;
+import com.yhlxj.dao.model.entity.User;
+import com.yhlxj.dao.model.entity.UserBindDetail;
+import com.yhlxj.service.user.UserBindRelationService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/*
+ *项目名: netflix
+ *文件名: UserBindRelationServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/8/1 15:03
+ */
+@Service
+@RequiredArgsConstructor
+public class UserBindRelationServiceImpl implements UserBindRelationService {
+
+	private final UserBindDetailMapper userBindDetailMapper;
+
+	private final UserMapper userMapper;
+
+	@Override
+	public List<Long> getRelationUserIdList(Long userId, User selectUser) {
+		List<Long> userIds = new ArrayList<>();
+		User user = Optional.ofNullable(selectUser).orElse(userMapper.selectById(userId));
+		if (user == null) throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID);
+
+		UserBindDetail userBindDetail;
+		//微信用户
+		if (StrUtil.isNotBlank(user.getUnionid()) || StrUtil.isNotEmpty(user.getOpenId())) {
+			userIds.add(userId);
+			userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
+					.eq(UserBindDetail::getUserId, userId).last("limit 1"));
+			if (userBindDetail == null) {
+				return userIds;
+			}
+			if (userBindDetail.getPhoneUserId() != null) {
+				userIds.add(userBindDetail.getPhoneUserId());
+			}
+			if (userBindDetail.getEmailUserId() != null) {
+				userIds.add(userBindDetail.getEmailUserId());
+			}
+			return userIds;
+		}
+		Long phoneUserId = null;
+		Long emailUserId = null;
+		if (StrUtil.isNotBlank(user.getLoginPhone())) {
+			phoneUserId = user.getId();
+		}
+		if (StrUtil.isNotBlank(user.getEmail())) {
+			emailUserId = user.getId();
+		}
+		//非微信用户
+		userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
+				.eq(phoneUserId != null, UserBindDetail::getPhoneUserId, phoneUserId)
+				.eq(emailUserId != null, UserBindDetail::getEmailUserId, emailUserId)
+				.last("limit 1"));
+		if (userBindDetail == null) {
+			userIds.add(userId);
+			return userIds;
+		}
+		phoneUserId = userBindDetail.getPhoneUserId();
+		emailUserId = userBindDetail.getEmailUserId();
+		Long wxUserId = userBindDetail.getUserId();
+		if (wxUserId != null) {
+			userIds.add(wxUserId);
+		}
+		if (phoneUserId != null) {
+			userIds.add(phoneUserId);
+		}
+		if (emailUserId != null) {
+			userIds.add(emailUserId);
+		}
+		return userIds;
+	}
+}

+ 67 - 0
luma/src/main/java/com/yhlxj/util/COSUtil.java

@@ -0,0 +1,67 @@
+package com.yhlxj.util;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.model.ObjectMetadata;
+import com.qcloud.cos.region.Region;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author zwhui
+ * @date 2024/7/18 14:29
+ */
+public class COSUtil {
+    /**
+     * 腾讯云COS
+     */
+    public static String SECRET_ID = "AKIDUzYL8rV9kEaoy0tcQH3kR9obeqDlw6ZK"; //这个参数需要你替换为自己的
+    public static String SECRET_KEY = "258AQj1DPcvTkEtXIOZrKtcSfvhiibUN"; //这个参数需要你替换为自己的
+    public static String BUCKET_NAME = "luma-1310013563"; //这个参数需要你替换为自己的
+    public static String REGION = "ap-hongkong"; //这个参数需要你替换为自己的
+
+    public static String prePath="conversation/"; //这个参数需要你替换为自己的
+
+    /**
+     * 获取客户端对象
+     * @return
+     */
+    public static COSClient getInstance(){
+        COSCredentials cred = new BasicCOSCredentials(SECRET_ID, SECRET_KEY);
+        Region region = new Region(REGION);
+        ClientConfig clientConfig = new ClientConfig(region);
+        return new COSClient(cred, clientConfig);
+    }
+
+    /**
+     * 视频上传
+     * @param in
+     * @param ext
+     * @param contentType
+     * @return
+     */
+    public static String upLoadVideo(InputStream in, String ext, String contentType) throws IOException {
+        ObjectMetadata objectMetadata = new ObjectMetadata();
+        String key = prePath + getTimeStamp() + ext;
+        objectMetadata.setContentType(contentType);
+        objectMetadata.setContentLength(in.available());
+        COSClient instance = getInstance();
+        instance.putObject(BUCKET_NAME, key, in, objectMetadata);
+        String url = "https://luma.galaxydvd.com/" + key;
+        instance.shutdown();
+        return url;
+    }
+
+    // 获得时间戳
+    public static String getTimeStamp() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
+        return LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
+    }
+
+}

+ 110 - 0
luma/src/main/java/com/yhlxj/util/SpringCtxUtils.java

@@ -0,0 +1,110 @@
+package com.yhlxj.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName SpringCtxUtils
+ * @Author chan
+ * @Date 2020-04-27 16:19
+ * @Description
+ */
+@Component
+public final class SpringCtxUtils implements ApplicationContextAware {
+
+    private static ApplicationContext context;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringCtxUtils.context = applicationContext;
+    }
+
+    public static ApplicationContext getContext() {
+        return context;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param beanIdOrName bean name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException 异常
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String beanIdOrName) throws BeansException {
+        return StringUtils.isEmpty(beanIdOrName) ? null : (T) context.getBean(beanIdOrName);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clazz {@link Class}
+     * @return T
+     * @throws BeansException 异常
+     */
+    public static <T> T getBean(Class<T> clazz) throws BeansException {
+        return clazz == null ? null : (T) context.getBean(clazz);
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name bean name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return context.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name bean name
+     * @return boolean
+     * @throws NoSuchBeanDefinitionException 异常
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return context.isSingleton(name);
+    }
+
+    /**
+     * @param name bean name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException 异常
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return context.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name bean name
+     * @return String array
+     * @throws NoSuchBeanDefinitionException 异常
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return context.getAliases(name);
+    }
+
+    /**
+     * <pre>
+     *     静默获取bean,无视异常
+     * </pre>
+     *
+     * @param clazz
+     * @param <T>
+     * @return
+     */
+    public static <T> T getBeanQuietly(Class<T> clazz) {
+        try {
+            return clazz == null ? null : (T) context.getBean(clazz);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 87 - 0
luma/src/main/java/com/yhlxj/util/VideoCoverExtractor.java

@@ -0,0 +1,87 @@
+package com.yhlxj.util;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.cyksj.common.util.StringUtil;
+import org.bytedeco.javacv.FFmpegFrameGrabber;
+import org.bytedeco.javacv.Java2DFrameConverter;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VideoCoverExtractor {
+
+	public static String extractVideoCoverFrame(Path tempFile, String outputImagePath, int frameNumber) throws IOException {
+		FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempFile.toFile());
+		grabber.start();
+
+		// 跳转到指定的帧
+		grabber.setFrameNumber(frameNumber);
+
+		// 抓取帧并转换为 BufferedImage
+		Java2DFrameConverter converter = new Java2DFrameConverter();
+		BufferedImage image = converter.getBufferedImage(grabber.grabImage());
+
+		File file = new File(outputImagePath);
+		File parentFile = file.getParentFile();
+		if (!parentFile.exists()) {
+			parentFile.mkdir();
+		}
+		// 保存图像到文件
+		ImageIO.write(image, "jpg", file);
+
+		grabber.stop();
+		grabber.release();
+
+		String picUrl = uploadPic(file);
+		if (file.exists()) {
+			file.delete();
+		}
+		return picUrl;
+	}
+
+	public static String extractVideoCoverFrame(Path tempFile) throws IOException {
+
+		return extractVideoCoverFrame(tempFile, "video/cover" + RandomUtil.randomString(5), 0);
+	}
+
+	private static String uploadPic(File file) {
+		try {
+			Map<String, Object> paramMap = new HashMap<>();
+			paramMap.put("file", file);
+			JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
+			return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
+		} catch (Exception e) {
+			System.out.println("上传图片错误:{}" + StringUtil.getErrorText(e));
+		}
+		return null;
+	}
+
+	public static String extractVideoCoverFrameByUrl(String url) throws IOException {
+		byte[] body;
+		try {
+			body = HttpUtil.downloadBytes(url);
+		} catch (Exception e) {
+			throw new IOException(e);
+		}
+
+		// 创建临时文件并将图像字节写入文件
+		Path tempFile = Files.createTempFile("conversation", ".mp4");
+		try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
+			fos.write(body);
+		}
+
+		String coverFrame = extractVideoCoverFrame(tempFile);
+		Files.deleteIfExists(tempFile);
+		return coverFrame;
+	}
+}

+ 127 - 0
luma/src/main/java/com/yhlxj/web/GlobalExceptionHandler.java

@@ -0,0 +1,127 @@
+package com.yhlxj.web;
+
+import cn.hutool.core.lang.Validator;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayApiCode;
+import com.cyksj.enums.GatewayResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @description 异常全局拦截
+ * @author chan
+ * @date 2021-03-22 9:51 下午
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    @Autowired
+    protected HttpServletRequest request;
+
+    private final static List<String> token = List.of("token无效", "token已过期");
+
+    @ExceptionHandler(value = {Exception.class})
+    public Result<String> handleNoteException(Exception e) {
+        StringBuilder builder = new StringBuilder(32);
+        builder.append(request.getRequestURI()).append(" -- > ");
+
+        StackTraceElement[] trace = e.getStackTrace();
+        log.info("{}",e);
+        for (StackTraceElement element : trace) {
+            if (StringUtil.startsWith(element.getClassName(), "com.cyksj")) {
+                builder.append(element.getFileName())
+                        .append("[").append(element.getLineNumber()).append("]")
+                        .append("&");
+            }
+        }
+        builder.deleteCharAt(builder.length() - 1);
+
+        String message = e.getMessage();
+        String value = builder.toString();
+        GatewayApiCode apiCode = null;
+        if (e instanceof BusinessRuntimeException) {
+            BusinessRuntimeException ee = (BusinessRuntimeException) e;
+            String code = ee.getCode();
+            if (Validator.isNumber(code)) {
+                if (GatewayApiCode.CMS_TOKEN_VALID.getCode().equals(Integer.parseInt(code))) {
+                    apiCode = GatewayApiCode.CMS_TOKEN_VALID;
+                }
+            }
+        }
+        log.error("全局错误处理: \n Cause: {} \n Value: {}", message, value);
+        if (apiCode != null) {
+            return GatewayResponse.FAIL.newBuilder().buildGatewayCode(apiCode).toResult();
+        }
+        return GatewayResponse.FAIL.newBuilder().setMsg(message).toResult(value);
+    }
+
+    @ExceptionHandler(value = ConstraintViolationException.class)
+    public Result<String> ConstraintViolationException(ConstraintViolationException ex) {
+        String message = "";
+        // 使用TreeSet是为了让输出的内容有序输出(默认验证的顺序是随机的)
+        Set<String> errorInfoSet = new TreeSet<String>();
+        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
+        if (!violations.isEmpty()) {
+            for (ConstraintViolation<?> item : violations) {
+                System.out.println(item.getPropertyPath());
+                // 遍历错误字段信息
+                errorInfoSet.add(item.getMessage());
+            }
+
+            StringBuilder sbf = new StringBuilder();
+            for (String errorInfo : errorInfoSet) {
+                sbf.append(errorInfo);
+                sbf.append(",");
+            }
+            message = sbf.substring(0, sbf.length() - 1);
+        }
+
+        log.error("错误字段 信息: \n Value: {}", message);
+
+        return GatewayResponse.FAIL.newBuilder().buildGatewayCode(GatewayApiCode.PARAMETER_MISSING_ERROR).setMsg(message).toResult();
+    }
+
+    @ExceptionHandler(BindException.class)
+    public Result<String> BindException(BindException bindingResult) {
+
+        // 验证参数信息是否有效
+        if (bindingResult.hasErrors()) {
+            // 获取错误字段信息集合
+            List<FieldError> fieldErrorList = bindingResult.getFieldErrors();
+
+            // 使用TreeSet是为了让输出的内容有序输出(默认验证的顺序是随机的)
+            Set<String> errorInfoSet = new TreeSet<String>();
+            for (FieldError fieldError : fieldErrorList) {
+                // 遍历错误字段信息
+                errorInfoSet.add(fieldError.getDefaultMessage());
+            }
+
+            StringBuilder sbf = new StringBuilder();
+            for (String errorInfo : errorInfoSet) {
+                sbf.append(errorInfo);
+                sbf.append(",");
+            }
+            String message = sbf.substring(0, sbf.length() - 1);
+
+            log.error("验证参数信息是否有效 错误信息: \n Value: {}", message);
+
+            return GatewayResponse.FAIL.newBuilder().buildGatewayCode(GatewayApiCode.PARAMETER_MISSING_ERROR).setMsg(message).toResult();
+
+        }
+        return GatewayResponse.SUCCESS.newBuilder().buildGatewayCode(GatewayApiCode.SUCCESS).setMsg("参数校验通过").toResult();
+    }
+}

+ 52 - 0
luma/src/main/java/com/yhlxj/web/config/JacksonConfig.java

@@ -0,0 +1,52 @@
+package com.yhlxj.web.config;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+
+/**
+ * @ClassName JacksonConfig
+ * @Author chan
+ * @Date 2020-04-24 15:21
+ * @Description json 转换
+ */
+@Configuration
+public class JacksonConfig {
+    @Bean
+    @Primary
+    @ConditionalOnMissingBean(ObjectMapper.class)
+    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
+        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
+        SimpleModule simpleModule = new SimpleModule();
+
+        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+        objectMapper.registerModule(simpleModule);
+        simpleModule.addSerializer(BigDecimal.class, new JsonSerializer<>() {
+            @Override
+            public void serialize(BigDecimal decimal, JsonGenerator gen, SerializerProvider serializers)
+                    throws IOException {
+                gen.writeString(decimal.toPlainString());
+            }
+        });
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        objectMapper.setDateFormat(format);
+        objectMapper.registerModule(simpleModule);
+        //通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化,属性为NULL 不序列化
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        return objectMapper;
+    }
+
+}

+ 19 - 0
luma/src/main/java/com/yhlxj/web/config/WebSocketConfiguration.java

@@ -0,0 +1,19 @@
+package com.yhlxj.web.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+*@author chan
+*@date 2024/6/28 17:33
+*/
+@Configuration
+public class WebSocketConfiguration {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+
+}

+ 256 - 0
luma/src/main/java/com/yhlxj/web/mirror/LumaController.java

@@ -0,0 +1,256 @@
+package com.yhlxj.web.mirror;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.IoKit;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.dto.SubmitVideoDTO;
+import com.cyksj.model.views.LumaUserConversationView;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapUtils;
+import com.yhlxj.dao.mapper.midjourney.LumaUserMapper;
+import com.yhlxj.dao.model.entity.LumaUser;
+import com.yhlxj.dao.model.entity.LumaUserConversation;
+import com.yhlxj.dao.model.response.SubmitResult;
+import com.yhlxj.redis.RedisService;
+import com.yhlxj.service.midjourney.LumaService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+
+/** server/镜像服务/luma文生视频
+ * @author zwhui
+ * @date 2024/4/23 10:31
+ */
+@Slf4j
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/applets/luma")
+public class LumaController {
+
+    @Value("${luma.videoUrl}")
+    private String lumaVideoUrl;
+
+    private final HttpServletResponse response;
+
+    private final LumaUserMapper lumaUserMapper;
+
+    private final HttpServletRequest request;
+
+    private final LumaService lumaService;
+
+    private final BeanSearcher beanSearcher;
+
+    private final RedisService redisService;
+
+    public LumaUser getUser() throws IOException {
+        String userToken = request.getHeader("user-token");
+        String ip = ServletUtil.getClientIP(request);
+        log.info("用户请求 ip:{}, userToken:{}", ip, userToken);
+        if (StringUtils.isBlank(userToken)) {
+            response.sendRedirect("https://nf.video");
+        }
+       return lumaService.getUser(userToken);
+    }
+
+    @GetMapping("/lumaMirrorWithToken/{userToken}")
+    public void lumaMirrorWithToken(@PathVariable String userToken) throws IOException {
+        Cookie cookie = new Cookie("userToken", userToken);
+        cookie.setMaxAge(31536000);
+        cookie.setPath("/");
+        response.addCookie(cookie);
+        response.sendRedirect(lumaVideoUrl);
+    }
+
+
+    /**
+     * 查询用户信息
+     */
+    @GetMapping("/whoami")
+    public Result<LumaUser> queryUser() throws IOException {
+        LumaUser user = getUser();
+        return GatewayResponse.SUCCESS.newBuilder().toResult(user);
+    }
+
+    /**
+     * 提交视频任务
+     */
+    @PostMapping("/submit/video")
+    public Result<SubmitResult> submitVideo(@RequestBody SubmitVideoDTO videoDTO) throws IOException {
+        log.info("提交video任务,提示:{}",videoDTO.getPrompt());
+        if (ObjectUtil.isAllEmpty(videoDTO.getPrompt(), videoDTO.getPicture())) {
+            throw BusinessRuntimeException.getInstance("请输入描述或上传图片");
+        }
+
+        if (StrUtil.isNotBlank(videoDTO.getPrompt()) && Validator.isChinese(videoDTO.getPrompt())) {
+            throw BusinessRuntimeException.getInstance("请输入英文格式内容");
+        }
+        LumaUser user = getUser();
+        Long num = checkUserLimit(user);
+        if (num != null && num < 0){
+            throw BusinessRuntimeException.getInstance("次数已用完");
+        }
+        SubmitResult conversation;
+        try {
+            conversation = lumaService.submitVideo(user, videoDTO, num);
+        } catch (Exception e) {
+            recoverUserLimit(user.getId());
+            log.error("提交video任务异常,{}", StringUtil.getErrorText(e));
+            throw BusinessRuntimeException.getInstance(e.getMessage());
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
+    }
+
+    /**
+     * 查看单个任务
+     */
+    @GetMapping("/get/task/info")
+    public Result<SubmitResult> getVideoTaskInfo(Long rid) throws Exception {
+        LumaUser user = getUser();
+        SubmitResult submitResult = lumaService.getVideoTaskInfoByTaskId(user, rid);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(submitResult);
+    }
+
+    /**
+     * 视频详情
+     */
+    @GetMapping("/get/detail")
+    public Result<LumaUserConversation> getVideoConversationByTid(Long rid) throws IOException {
+        LumaUser user = getUser();
+        LumaUserConversation conversation = lumaService.getVideoConversationByTid(user, rid);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
+    }
+
+    /**
+     * 查看多个任务
+     */
+    @GetMapping("/conversation/tasks")
+    public Result<List<LumaUserConversation>> conversationListByIds(@RequestParam(value = "ids" ) List<Long> ids) throws Exception {
+        LumaUser user = getUser();
+        if (CollUtil.isEmpty(ids)) {
+            return GatewayResponse.SUCCESS.newBuilder().toResult();
+        }
+        log.info("根据ids查询会话 userId:{}",user.getId());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(lumaService.conversationListByIds(ids, user));
+    }
+
+    /**
+     * 回调
+     */
+    @PostMapping("/video/notify")
+    public void submitVideoNotify(HttpServletRequest request) throws IOException {
+        log.info("提交视频接口回调notifyHook");
+        InputStream inputStream = request.getInputStream();
+        byte[] bytes = IoKit.toBytes(inputStream);
+        String json = new String(bytes, StandardCharsets.UTF_8);
+        inputStream.close();
+        lumaService.submitVideoNotify(json);
+    }
+
+    /**
+     * 扩展视频
+     */
+    @PostMapping("/video/extended")
+    public Result<SubmitResult> extendedVideo(@RequestBody SubmitVideoDTO videoDTO) throws Exception {
+        String taskId = videoDTO.getTaskId();
+        if (StrUtil.isEmpty(taskId)) {
+            throw BusinessRuntimeException.getInstance("");
+        }
+        String prompt = videoDTO.getPrompt();
+        if (StrUtil.isEmpty(prompt)) {
+            throw BusinessRuntimeException.getInstance("请输入描述内容");
+        }
+        if (StrUtil.isNotBlank(videoDTO.getPrompt()) && Validator.isChinese(videoDTO.getPrompt())) {
+            throw BusinessRuntimeException.getInstance("请输入英文格式内容");
+        }
+        LumaUser user = getUser();
+        Long num = checkUserLimit(user);
+        if (num != null && num < 0) {
+            throw BusinessRuntimeException.getInstance("次数已用完");
+        }
+        SubmitResult submitResult;
+        try {
+            log.info("userId:{}扩展taskId:{}视频", user.getId(), taskId);
+            submitResult = lumaService.extendedVideo(user, videoDTO);
+        } catch (Exception e) {
+            recoverUserLimit(user.getId());
+            log.error("扩展taskId:{}视频任务异常,{}", taskId, StringUtil.getErrorText(e));
+            throw BusinessRuntimeException.getInstance(e.getMessage());
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult(submitResult);
+    }
+
+    /**
+     * 更新任务缩量图
+     */
+    @PutMapping("/update/cover/{id}")
+    public Result<String> updateCover(@PathVariable Long id) throws IOException {
+        LumaUser user = getUser();
+        String cover = lumaService.updateCover(id,user.getId());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(cover);
+    }
+
+    /**
+     * 我的luma作品
+     */
+    @GetMapping("/get")
+    public Result<SearchResult<LumaUserConversationView>> getPaintingCollect() throws IOException {
+        LumaUser user = getUser();
+        SearchResult<LumaUserConversationView> search = beanSearcher.search(LumaUserConversationView.class, MapUtils.flatBuilder(request.getParameterMap())
+                .field(LumaUserConversationView::getUserId, user.getId())
+                .build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+    }
+
+
+    /**
+     * 检查用户次数
+     */
+    public Long checkUserLimit(LumaUser user) {
+        Long num = 0L;
+        if (user.getExpireTime().before(new Date())) {
+            throw BusinessRuntimeException.getInstance("账号已过期");
+        }
+        if (user.getNum() == 0) {
+            throw BusinessRuntimeException.getInstance("无可用次数");
+        }
+        num = redisService.decr(RedisService.key.LUMA_NUM_LIMIT.getName() + user.getId(), 1L);
+
+        if (num < 0) {
+            redisService.incr(RedisService.key.LUMA_NUM_LIMIT.getName() + user.getId(), 1L);
+        } else {
+            int i = lumaUserMapper.decLumaNum(user.getId(), 1, user.getNum());
+            if (i == 0) {
+                redisService.incr(RedisService.key.LUMA_NUM_LIMIT.getName() + user.getId(), 1L);
+                throw BusinessRuntimeException.getInstance("网络异常,请稍后再试");
+            }
+        }
+        return num;
+    }
+
+    /**
+     * 恢复次数
+     */
+    public void recoverUserLimit(Long id){
+        redisService.incr(RedisService.key.LUMA_NUM_LIMIT.getName() + id, 1L);
+        lumaUserMapper.incrNum(id, 1);
+    }
+}

+ 165 - 0
luma/src/main/java/com/yhlxj/web/wss/LumaServerEndpoint.java

@@ -0,0 +1,165 @@
+package com.yhlxj.web.wss;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.yhlxj.dao.model.entity.LumaUser;
+import com.yhlxj.dao.model.entity.LumaUserConversation;
+import com.yhlxj.dao.model.enums.WsMessageTypeEnum;
+import com.yhlxj.service.midjourney.LumaService;
+import com.yhlxj.service.midjourney.impl.LumaServiceImpl;
+import com.yhlxj.util.SpringCtxUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author chan
+ * @date 2024/6/28 13:59
+ */
+@Component
+@ServerEndpoint("/ws/luma/{userToken}/{taskId}")
+@Slf4j
+public class LumaServerEndpoint {
+    private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+    //webSocket
+    public static Map<Long, WssSession> WSS_SESSION_MAP = new ConcurrentHashMap<>();
+
+    /**
+     * 连接建立回调
+     *
+     * @param session
+     */
+    @OnOpen
+    public void onOpen(Session session, @PathParam("userToken") String userToken, @PathParam("taskId") String taskId) throws IOException {
+        log.info("[WS建立连接],token:{}, taskId:{}", userToken, taskId);
+
+        session.setMaxIdleTimeout(TimeUnit.MINUTES.toMillis(60));
+        //检查uniodId是否合法
+        if (StringUtils.isBlank(userToken) || StringUtils.isBlank(taskId)) {
+
+            log.error("[WS建立连接失败],[参数非法],{},{}", userToken, taskId);
+
+            session.getBasicRemote().sendText("入参错误");
+            session.close();
+        }
+        LumaUser user = getUser(userToken);
+        if (user == null) {
+            log.error("[WS建立连接失败],[用户过期],{},{}", userToken, taskId);
+            session.getBasicRemote().sendText("用户过期");
+            session.close();
+        } else {
+            LumaService lumaService = SpringCtxUtils.getBean(LumaServiceImpl.class);
+            LumaUserConversation conversation = lumaService.getConversationById(taskId);
+            WssSession wsSession = new WssSession(session);
+            if (conversation == null) {
+                wsSession.sendMessage(WsMessageTypeEnum.SERVER_TASK_NOT_FIND, "任务不存在.");
+            } else {
+                WSS_SESSION_MAP.put(user.getId(), wsSession);
+                wsSession.sendMessage(WsMessageTypeEnum.SERVER_TASK_INFO, new JSONObject(conversation));
+            }
+
+        }
+
+    }
+
+    /**
+     * 收到客户端消息回调
+     *
+     * @param message
+     * @param session
+     */
+    @OnMessage
+    public void OnMessage(String message, Session session, @PathParam("userToken") String userToken, @PathParam("taskId") String taskId) {
+        try {
+            log.info("[WS收到消息],{},{},{}", userToken, taskId, message);
+
+            WssSession wsSession = new WssSession(session);
+            JSONObject body = null;//消息具体内容
+
+            //处理消息类型,是轮询还是消息
+            switch (WsMessageTypeEnum.getMessageType(message.split(":")[0])) {
+                //ping pong
+                case PING:
+                    wsSession.pong();
+                    return;
+                case MESSAGE:
+                    body = JSONUtil.parseObj(message.substring(8));
+                    break;
+                default:
+                    log.error("[WS消息类型异常],{},{},{}", userToken, taskId, message);
+                    return;
+            }
+
+            //根据子类型进行处理
+            switch (WsMessageTypeEnum.getMessageType(body.getStr("type"))) {
+
+                case CLIENT_TASK_ID: //首次查看任务状态
+                    LumaService lumaService = SpringCtxUtils.getBean(LumaService.class);
+                    lumaService.getConversationById(taskId);
+                    break;
+
+                case CLIENT_CLOSE:
+                    log.info("[WS连接关闭],{},{}", userToken, taskId);
+                    //dailyQuizService.close(uniodId);
+                    break;
+
+                default:
+                    log.error("[WS消息子类型异常],{},{},{}", userToken, taskId, message);
+
+            }
+
+        } catch (Exception e) {
+            log.error("[WS接收消息失败],{},{},{},{}", userToken, taskId, message, e);
+        }
+    }
+
+    /**
+     * 发生错误回调
+     *
+     * @param session
+     * @param error
+     */
+    @OnError
+    public void OnError(Session session, Throwable error, @PathParam("userToken") String userToken, @PathParam("taskId") String taskId) throws IOException {
+        log.error("[WS ONERROR],{}", error.getMessage());
+        if (session.isOpen()) {
+            session.close();
+        }
+        log.info("[WS ONERROR],移除连接池内连接");
+        LumaUser user = getUser(userToken);
+        WSS_SESSION_MAP.remove(user.getId());
+    }
+
+    /**
+     * 关闭连接回调
+     *
+     * @param session
+     */
+    @OnClose
+    public void OnClose(Session session, @PathParam("userToken") String userToken, @PathParam("taskId") String taskId) throws IOException {
+
+        log.info("[WS连接关闭],{},{}", userToken, taskId);
+        if (session.isOpen()) {
+            session.close();
+        }
+        log.info("[WS连接关闭],移除连接池内连接");
+        LumaUser user = getUser(userToken);
+        WSS_SESSION_MAP.remove(user.getId());
+    }
+
+    public LumaUser getUser(String userToken) {
+        LumaService lumaService = SpringCtxUtils.getBean(LumaServiceImpl.class);
+        return lumaService.getUser(userToken);
+    }
+
+}

+ 13 - 0
luma/src/main/java/com/yhlxj/web/wss/WssSendable.java

@@ -0,0 +1,13 @@
+package com.yhlxj.web.wss;
+
+import com.yhlxj.dao.model.enums.WsMessageTypeEnum;
+
+import java.io.IOException;
+
+public interface WssSendable {
+    void sendMessage(WsMessageTypeEnum type, Object object) throws IOException;
+
+    void close() throws IOException;
+
+    boolean isOpen() throws IOException;
+}

+ 143 - 0
luma/src/main/java/com/yhlxj/web/wss/WssSession.java

@@ -0,0 +1,143 @@
+package com.yhlxj.web.wss;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.yhlxj.dao.model.enums.WsMessageTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.websocket.*;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author chan
+ * @date 2024/6/27 18:40
+ */
+@Data
+@AllArgsConstructor
+public class WssSession implements WssSendable , Serializable {
+    private static Logger logger = LoggerFactory.getLogger(WssSession.class);
+
+    private Session session;
+
+    /**
+     * 发送自定义类型消息
+     *
+     * @param type
+     * @param object
+     */
+    @Override
+    public void sendMessage(WsMessageTypeEnum type, Object object) {
+        if (session.isOpen()) {
+            //组织成形如 message:{"type":"type", "data":{...}}想
+            JSONObject result = new JSONObject();
+            result.putOpt("type", type.type);
+            result.putOpt("content", JSONUtil.toJsonStr(object));
+
+            logger.info("[WS-SENDMESSAGE],{}", WsMessageTypeEnum.MESSAGE.type + ":" + result.toString());
+            try {
+                getSession().getBasicRemote().sendText(WsMessageTypeEnum.MESSAGE.type + ":" + result.toString());
+            } catch (Exception e) {
+                logger.info("[WS-SENDMESSAGE],[失败],{}", WsMessageTypeEnum.MESSAGE.type + ":" + result.toString());
+            }
+        }
+    }
+
+    /**
+     * 发送pong
+     *
+     * @throws IOException
+     */
+    public void pong() throws IOException {
+        getSession().getBasicRemote().sendText(WsMessageTypeEnum.PONG.type);
+    }
+
+
+    //-----------------DELEGATOR---------------------
+
+    public WebSocketContainer getContainer() {
+        return session.getContainer();
+    }
+
+    public void addMessageHandler(MessageHandler messageHandler) throws IllegalStateException {
+        session.addMessageHandler(messageHandler);
+    }
+
+    public Set<MessageHandler> getMessageHandlers() {
+        return session.getMessageHandlers();
+    }
+
+    public void removeMessageHandler(MessageHandler messageHandler) {
+        session.removeMessageHandler(messageHandler);
+    }
+
+    public String getProtocolVersion() {
+        return session.getProtocolVersion();
+    }
+
+    public String getNegotiatedSubprotocol() {
+        return session.getNegotiatedSubprotocol();
+    }
+
+    public List<Extension> getNegotiatedExtensions() {
+        return session.getNegotiatedExtensions();
+    }
+
+    public boolean isSecure() {
+        return session.isSecure();
+    }
+
+    public boolean isOpen() {
+        return session.isOpen();
+    }
+
+    public long getMaxIdleTimeout() {
+        return session.getMaxIdleTimeout();
+    }
+
+    public void setMaxIdleTimeout(long l) {
+        session.setMaxIdleTimeout(l);
+    }
+
+    public void setMaxBinaryMessageBufferSize(int i) {
+        session.setMaxBinaryMessageBufferSize(i);
+    }
+
+    public int getMaxBinaryMessageBufferSize() {
+        return session.getMaxBinaryMessageBufferSize();
+    }
+
+    public void setMaxTextMessageBufferSize(int i) {
+        session.setMaxTextMessageBufferSize(i);
+    }
+
+    public int getMaxTextMessageBufferSize() {
+        return session.getMaxTextMessageBufferSize();
+    }
+
+    public RemoteEndpoint.Async getAsyncRemote() {
+        return session.getAsyncRemote();
+    }
+
+    public RemoteEndpoint.Basic getBasicRemote() {
+        return session.getBasicRemote();
+    }
+
+    public String getId() {
+        return session.getId();
+    }
+
+    public void close() throws IOException {
+        session.close();
+    }
+
+    public void close(CloseReason closeReason) throws IOException {
+        session.close(closeReason);
+    }
+
+}

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
luma/src/main/resources/application-dev.yml


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
luma/src/main/resources/application-prd.yml


+ 106 - 0
luma/src/main/resources/application-pre.yml

@@ -0,0 +1,106 @@
+server:
+  port: 8083
+  undertow:
+    buffer-size: 1024
+    direct-buffers: false
+  max-http-header-size: 8192
+
+spring:
+  servlet:
+    multipart:
+      max-file-size: 50MB
+      max-request-size: 50MB
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    url: jdbc:mysql://sh-cdb-4tdnmrqa.sql.tencentcdb.com:63893/yhlxj_pre?useAffectedRows=true&serverTimezone=Asia/Shanghai
+    username: yhlxj
+    password: yhlxj123
+    platform: mysql
+    continue-on-error: false
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    hikari:
+      pool-name: hikariCP-mysql-
+      maximum-pool-size: 10
+      minimum-idle: 6
+      idle-timeout: 180000
+      max-lifetime: 600000
+      read-only: false
+      connection-timeout: 30000
+      connection-test-query: SELECT 1
+      # # 以下配置推荐mysql 5.7.27+ 或 8.0.18+
+      # https://cdn.oreillystatic.com/en/assets/1/event/21/Connector_J%20Performance%20Gems%20Presentation.pdf
+      data-source-properties: {
+        useSSL: false,
+        useAffectedRows: true,
+        useUnicode: true,
+        characterEncoding: utf8,
+        connectTimeout: 30000,
+        socketTimeout: 20000,
+
+        # 服务端缓存PrepStmts
+        useServerPrepStmts: true,
+        cachePrepStmts: true,
+        # 官方推荐250-500
+        prepStmtCacheSize: 512,
+        # 官方推荐2048
+        prepStmtCacheSqlLimit: 2048,
+
+        cacheServerConfiguration: true,
+        useLocalSessionState: true,
+        rewriteBatchedStatements: true,
+        useLocalTransactionState: true,
+        cacheResultSetMetadata: true,
+        elideSetAutoCommits: true,
+        maintainTimeStats: false,
+      }
+  redis:
+    # Redis数据库索引(默认为 0)
+    database: 3
+    # Redis服务器地址
+    host: 175.24.198.76
+    # Redis服务器连接端口
+    port: 63222
+    # Redis 密码
+    password: yinhe9913
+    lettuce:
+      pool:
+        # 连接池中的最小空闲连接
+        min-idle: 8
+        # 连接池中的最大空闲连接
+        max-idle: 500
+        # 连接池最大连接数(使用负值表示没有限制)
+        max-active: 2000
+        # 连接池最大阻塞等待时间(使用负值表示没有限制)
+        max-wait: 10000
+    # 连接超时时间(毫秒)
+    timeout: 5000
+
+
+logging:
+  config: classpath:config/logback.xml
+
+#mybatis
+mybatis:
+  mapper-locations: classpath:mapper/*Mapper.xml
+  executor-type: simple
+  type-aliases-package: com.cyksj.dao.model
+  configuration:
+    local-cache-scope: statement
+    cache-enabled: false
+
+#Bean Searcher 分页配置
+bean-searcher:
+  params:
+    pagination:
+      default-size: 10
+      page: start
+      size: limit
+      start: 1
+
+midjourney:
+  url: https://mj.galaxydvd.com
+  drawUrl: https://mj.galaxydvd.com/draw/web/
+
+luma:
+  url: https://luma.galaxydvd.com
+  videoUrl: https://luma.galaxydvd.com/videoTest/web

+ 4 - 0
luma/src/main/resources/application.yml

@@ -0,0 +1,4 @@
+spring:
+  profiles:
+    active:
+      - ${activeProfile}

+ 111 - 0
luma/src/main/resources/config/logback-debug.xml

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+	<include resource="org/springframework/boot/logging/logback/defaults.xml" />
+	<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
+
+	<contextName>midjourney-server</contextName>
+
+	<property name="log_home" value="./logs"/>
+
+	<!-- 控制台输出 -->
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<!-- 日志输出的文件的格式 -->
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+
+	</appender>
+	<!-- 按照每天生成日志文件 -->
+	<appender name="ROOT-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender" level="INFO">
+		<append>true</append>
+
+        <!-- 过滤器,只记录 warn 和 info 级别的日志 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>NEUTRAL</onMismatch>
+        </filter>
+
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+
+	    <file>${log_home}/midjourney-default.log</file>
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+			<FileNamePattern>${log_home}/midjourney-default.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+			<!-- kb/mb/gb -->
+			<maxFileSize>128MB</maxFileSize>
+			<!-- 日志文件保留天数 -->
+			<maxHistory>14</maxHistory>
+		</rollingPolicy>
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+
+	</appender>
+
+	<!-- 按照每天生成日志文件 -->
+	<appender name="ERROR-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<append>true</append>
+
+		<!-- 过滤器,只记录 error 级别的日志 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>ERROR</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+
+		<!-- 日志名称 -->
+		<file>${log_home}/midjourney-error.log</file>
+		<!-- 每天生成一个日志文件,保存15天的日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+			<!--日志文件输出的文件名:按天回滚 daily -->
+			<FileNamePattern>${log_home}/midjourney-error.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+			<!-- kb/mb/gb -->
+			<maxFileSize>128MB</maxFileSize>
+			<!-- 日志文件保留天数 -->
+			<maxHistory>14</maxHistory>
+			<totalSizeCap>10GB</totalSizeCap>
+		</rollingPolicy>
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<!-- 编码 -->
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+
+	<!-- 异步输出 -->
+	<appender name="ERROR_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的. 如果discardingThreshold大于0,队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<queueSize>10240</queueSize>
+		<appender-ref ref="ERROR-APPENDER" />
+	</appender>
+
+    <!-- 异步输出 -->
+	<appender name="ROOT_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的. 如果discardingThreshold大于0,队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<queueSize>10240</queueSize>
+		<appender-ref ref="ROOT-APPENDER" />
+	</appender>
+
+    <logger name="jdbc.audit" level="WARN" />
+    <logger name="jdbc.resultset" level="WARN" />
+    <logger name="jdbc.sqlonly" level="WARN" />
+    <logger name="jdbc.sqltiming" level="INFO" />
+    <logger name="jdbc.connection" level="WARN" />
+
+	<root level="INFO">
+		<appender-ref ref="STDOUT" />
+		<appender-ref ref="ERROR_ASYNC" />
+		<appender-ref ref="ROOT_ASYNC" />
+	</root>
+	
+	<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
+</configuration>

+ 105 - 0
luma/src/main/resources/config/logback.xml

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+	<include resource="org/springframework/boot/logging/logback/defaults.xml" />
+	<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
+
+	<contextName>midjourney-server</contextName>
+
+	<property name="log_home" value="./logs"/>
+
+	<!-- 控制台输出 -->
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<!-- 日志输出的文件的格式 -->
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+
+	</appender>
+	<!-- 按照每天生成日志文件 -->
+	<appender name="ROOT-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender" level="INFO">
+		<append>true</append>
+
+		<!-- 过滤器,只记录 warn 和 info 级别的日志 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>WARN</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>NEUTRAL</onMismatch>
+		</filter>
+
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>INFO</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+
+		<file>${log_home}/midjourney-default.log</file>
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+			<FileNamePattern>${log_home}/midjourney-default.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+			<!-- kb/mb/gb -->
+			<maxFileSize>128MB</maxFileSize>
+			<!-- 日志文件保留天数 -->
+			<maxHistory>14</maxHistory>
+		</rollingPolicy>
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+
+	</appender>
+
+	<!-- 按照每天生成日志文件 -->
+	<appender name="ERROR-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<append>true</append>
+
+		<!-- 过滤器,只记录 error 级别的日志 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>ERROR</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+
+		<!-- 日志名称 -->
+		<file>${log_home}/midjourney-error.log</file>
+		<!-- 每天生成一个日志文件,保存15天的日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+			<!--日志文件输出的文件名:按天回滚 daily -->
+			<FileNamePattern>${log_home}/midjourney.log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+			<!-- kb/mb/gb -->
+			<maxFileSize>128MB</maxFileSize>
+			<!-- 日志文件保留天数 -->
+			<maxHistory>14</maxHistory>
+			<totalSizeCap>10GB</totalSizeCap>
+		</rollingPolicy>
+		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | [%magenta(%thread) %green(%5L)] %highlight(%-5level) %cyan(%logger{50}) : %msg %n</pattern>
+			<!-- 编码 -->
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+
+	<!-- 异步输出 -->
+	<appender name="ERROR_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的. 如果discardingThreshold大于0,队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<queueSize>10240</queueSize>
+		<appender-ref ref="ERROR-APPENDER" />
+	</appender>
+
+	<!-- 异步输出 -->
+	<appender name="ROOT_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+		<!-- 不丢失日志.默认的. 如果discardingThreshold大于0,队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
+		<discardingThreshold>0</discardingThreshold>
+		<queueSize>10240</queueSize>
+		<appender-ref ref="ROOT-APPENDER" />
+	</appender>
+
+	<root level="INFO">
+		<appender-ref ref="STDOUT" />
+		<appender-ref ref="ERROR_ASYNC" />
+		<appender-ref ref="ROOT_ASYNC" />
+	</root>
+
+	<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
+</configuration>

+ 12 - 0
netflix-dao/src/main/java/com/cyksj/mapper/LumaUserMapper.java

@@ -0,0 +1,12 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.LumaUser;
+
+
+/**
+ * @author zwhui
+ * @date 2024/4/16 17:46
+ */
+public interface LumaUserMapper extends BaseMapper<LumaUser> {
+}

+ 42 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitVideoDTO.java

@@ -0,0 +1,42 @@
+package com.cyksj.model.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.util.List;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 13:49
+ */
+@Data
+public class SubmitVideoDTO {
+
+    /**
+     * 任务id
+     */
+    private String taskId;
+
+    /**
+     * 提示词
+     */
+    private String prompt;
+
+    /**
+     * 增强提示
+     */
+    private Boolean expandPrompt;
+
+    private String picture;
+
+    /**
+     * 结束帧
+     */
+    private String imageEndUrl;
+
+    /**
+     * 垫图base64数组
+     */
+    private List<String> base64Array;
+
+}

+ 21 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/BlendDimensions.java

@@ -0,0 +1,21 @@
+package com.cyksj.model.dto.dto;
+
+
+import lombok.Getter;
+
+@Getter
+public enum BlendDimensions {
+
+	PORTRAIT("2:3"),
+
+	SQUARE("1:1"),
+
+	LANDSCAPE("3:2");
+
+	private final String value;
+
+	BlendDimensions(String value) {
+		this.value = value;
+	}
+
+}

+ 35 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/MessageButton.java

@@ -0,0 +1,35 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+/**
+ * 可执行按钮
+ */
+@Data
+public class MessageButton {
+    /**
+     * 动作标识
+     */
+    private String customId;
+
+    /**
+     * 图标
+     */
+    private String emoji = "";
+
+    /**
+     * 文本
+     */
+    private String label;
+
+    /**
+     * 类型,系统内部使用
+     */
+    private int type = 2;
+
+    /**
+     * 样式: 2(Primary)、3(Green)
+     */
+    private int style = 2;
+
+}

+ 107 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/MidjourneyProxyAccountInfo.java

@@ -0,0 +1,107 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2024/7/9 16:58
+ */
+
+@Data
+public class MidjourneyProxyAccountInfo {
+    private String nijiMode;
+    private String lifetimeUsage;
+    private boolean remixAutoSubmit;
+    private String remark;
+    private String mjBotChannelId;
+    private Displays displays;
+    private int timeoutMinutes;
+    private String variation;
+    private String mode;
+    private String billedWay;
+    private long dateCreated;
+
+    private boolean enable;
+
+    private List<String> nijiButtons;
+    private boolean nijiRemix;
+    private String relaxedUsage;
+    private String stylize;
+    private String id;
+    private String channelId;
+    private String email;
+    private String nijiBotChannelId;
+    private long renewDate;
+    private VersionSelector versionSelector;
+    private String subscribePlan;
+    private int queueSize;
+    private List<Button> buttons;
+    private int coreSize;
+    private boolean raw;
+    private String userAgent;
+    private String sessionId;
+    private String guildId;
+    private String userId;
+    private boolean publicMode;
+    private String version;
+    private String fastTimeRemaining;
+    private String userToken;
+    private String name;
+    private Properties properties;
+    private boolean remix;
+
+
+
+    @Data
+    public static class Displays {
+        private String mode;
+        private String nijiMode;
+        private String subscribePlan;
+        private String billedWay;
+        private String dateCreated;
+        private String stylize;
+        private String variation;
+        private String renewDate;
+
+    }
+
+    @Data
+    public static class VersionSelector {
+        private List<Option> options;
+        private String placeholder;
+        private int type;
+        private String customId;
+
+
+        @Data
+        public static class Option {
+            private String emoji;
+            private String description;
+            private String label;
+            private String value;
+            private boolean selected;
+
+        }
+    }
+
+    @Data
+    public static class Button {
+        private String emoji;
+        private int style;
+        private String label;
+        private int type;
+        private String customId;
+
+    }
+
+    @Data
+    public static class Properties {
+        private List<String> supportApplicationIds;
+        private int flags;
+        private int weight;
+        private String messageId;
+
+    }
+}

+ 40 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/MjVersionStatus.java

@@ -0,0 +1,40 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+@Data
+public class MjVersionStatus {
+    //{
+    //    "name": "jobs.time_to_start.fast.10min.job_type_kdpt_diffusion_anime",
+    //        "date": "2024-07-11T03:24:01.784Z",
+    //        "value": "N/A"
+    //}
+
+    private String name;
+
+    private String date;
+
+    private String value;
+    /**
+     * 转换为毫秒
+     */
+    private Long millisecond;
+
+
+    public void setValue(String value) {
+        this.value = value;
+        // 转换为秒并设置 second
+        if (value != null && !value.equals("N/A")) {
+            try {
+                double minutes = Double.parseDouble(value);
+                this.millisecond = Math.round(minutes * 60 * 1000);
+            } catch (NumberFormatException e) {
+                this.millisecond = null;
+                System.err.println("Invalid value format: " + value);
+            }
+        } else {
+            this.millisecond = null;
+        }
+    }
+
+}

+ 33 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitActionDTO.java

@@ -0,0 +1,33 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author zwhui
+ * @date 2024/4/24 10:56
+ */
+@Data
+public class SubmitActionDTO {
+    /**
+     * 任务对应按钮的customId
+     */
+    @NotBlank(message = "customId参数不能为空")
+    private String customId;
+
+    /**
+     * 执行Imagine任务生成的ID
+     */
+    @NotNull(message = "任务ID不能为空")
+    private Long taskId;
+
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
+}

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitBlendDTO.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 13:49
+ */
+@Data
+public class SubmitBlendDTO {
+
+	/**
+	 * base64
+	 */
+	@NotEmpty(message = "请上传至少两张图片")
+	private List<String> base64Array;
+
+	/**
+	 * 比例: PORTRAIT(2:3); SQUARE(1:1); LANDSCAPE(3:2)
+	 */
+	private BlendDimensions dimensions = BlendDimensions.SQUARE;
+
+	/**
+	 * 机器人类型
+	 * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+	 * MID_JOURNEY
+	 * NIJI_JOURNEY
+	 */
+	private String botType = "MID_JOURNEY";
+	private Integer mode;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitDescribeDTO.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 13:49
+ */
+@Data
+public class SubmitDescribeDTO {
+
+	/**
+	 * base64
+	 */
+	@NotBlank(message = "请上传图片")
+	private String base64;
+
+	/**
+	 * 机器人类型
+	 * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+	 * MID_JOURNEY
+	 * NIJI_JOURNEY
+	 */
+	private String botType = "MID_JOURNEY";
+	private Integer mode;
+}

+ 9 - 1
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitImagineDTO.java → netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitImagineDTO.java

@@ -1,4 +1,4 @@
-package com.cyksj.model.dto;
+package com.cyksj.model.dto.dto;
 
 import lombok.Data;
 
@@ -23,4 +23,12 @@ public class SubmitImagineDTO {
      */
     private List<String> base64Array;
 
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
+    private Integer mode;
 }

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitModalDTO.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 18:04
+ */
+@Data
+public class SubmitModalDTO {
+
+    /**
+     * 局部重绘base64
+     */
+    private String maskBase64;
+
+    /**
+     * 提示词
+     */
+    private String prompt;
+
+    /**
+     * 任务id
+     */
+    @NotNull(message = "任务ID不能为空")
+    private Long taskId;
+    private Integer mode;
+
+    private Long beforeTaskId;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitSeedDTO.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author chan
+ * @date 2024/6/28 21:48
+ */
+@Data
+public class SubmitSeedDTO {
+
+    /**
+     * 执行Imagine任务生成的ID
+     */
+    @NotNull(message = "任务ID不能为空")
+    private Long taskId;
+
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
+    private Integer mode;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitShortenDTO.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 18:07
+ */
+@Data
+public class SubmitShortenDTO {
+
+    /**
+     * 提示词
+     */
+    @NotBlank(message = "提示词不能为空")
+    private String prompt;
+
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
+    private Integer mode;
+}

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/dto/SubmitUploadDTO.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.dto.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * @author zwhui
+ * @date 2024/4/23 13:49
+ */
+@Data
+public class SubmitUploadDTO {
+    /**
+     * 垫图base64数组
+     */
+    @NotEmpty(message = "请上传至少一张图片")
+    private List<String> base64Array;
+
+}

+ 53 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/LumaUser.java

@@ -0,0 +1,53 @@
+package com.cyksj.model.entity;
+
+/**
+ * @author zwhui
+ * @date 2024/4/16 17:01
+ */
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class LumaUser extends BaseEntity{
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 头像
+     */
+    private String img;
+
+    /**
+     * 用户凭证
+     */
+    private String userToken;
+
+    /**
+     * 过期时间
+     */
+    private Date expireTime;
+
+    /**
+     * 车票id
+     */
+    private Long relationId;
+
+    /**
+     * 可用次数
+     */
+    private Integer num;
+
+    /**
+     * 车票类型 2试用
+     */
+    @DbIgnore
+    @TableField(exist = false)
+    private Integer aqType;
+}

+ 82 - 0
netflix-dao/src/main/java/com/cyksj/model/views/LumaUserConversationView.java

@@ -0,0 +1,82 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: LumaUserConversationView
+ * 创建者: JavaZou
+ * 创建时间:2024/6/4 13:54
+ */
+@Getter
+@Setter
+@SearchBean(tables = "luma_user_conversation luc",
+		orderBy = "luc.created_time desc")
+public class LumaUserConversationView {
+
+	@DbField("luc.id")
+	private Long rid;
+
+	@DbField("luc.user_id")
+	private Long userId;
+
+
+	/**
+	 * 任务id
+	 */
+	@DbField("luc.task_id")
+	private String taskId;
+
+	/**
+	 * 开始时间
+	 */
+	@DbField("luc.start_time")
+	private Long startTime;
+
+	/**
+	 * 完成时间
+	 */
+	@DbField("luc.finish_time")
+	private Long finishTime;
+
+	/**
+	 * 描述
+	 */
+	@DbField("luc.prompt")
+	private String prompt;
+
+	/**
+	 * 任务状态,可用值:pending
+	 */
+	@DbField("luc.status")
+	private String status;
+
+	/**
+	 * 失败原因
+	 */
+	@DbField("luc.fail_reason")
+	private String failReason;
+
+	/**
+	 * 进度
+	 */
+	@DbField("luc.progress")
+	private String progress;
+
+	/**
+	 * 视频地址
+	 */
+	@DbField("luc.video_url")
+	private String videoUrl;
+
+	@DbField("luc.thumbnail")
+	private String thumbnail;
+
+	@DbField("luc.created_time")
+	private Date createdTime;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/views/LumaUserDetailView.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.views;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: LumaUserDetailView
+ * 创建者: JavaZou
+ * 创建时间:2024/6/25 15:26
+ */
+@Getter
+@Setter
+public class LumaUserDetailView {
+	private Integer useFastNum;
+
+	private Integer useRelaxNum;
+
+	private Integer mjFastNum;
+
+	private Integer mjRelaxNum;
+
+	private Long userId;
+
+	private Date expiryTime;
+}

+ 40 - 0
netflix-dao/src/main/java/com/cyksj/model/views/LumaUserView.java

@@ -0,0 +1,40 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @author zwhui
+ * @date 2024/4/28 17:43
+ */
+@Data
+@SearchBean(tables = "midjourney_user mu left join groups_relation gr on mu.relation_id = gr.id ")
+public class LumaUserView {
+
+
+    @DbField("mu.id")
+    private Long id;
+
+    @DbField("mu.relation_id")
+    private Long relationId;
+
+    @DbField("gr.user_id")
+    private Long userId;
+
+    @DbField("mu.mj_fast_num")
+    private Integer mjFastNum;
+
+    @DbField("mu.mj_relax_num")
+    private Integer mjRelaxNum;
+
+    @DbField("mu.expire_time")
+    private Date expireTime;
+
+
+
+
+
+}

+ 1 - 0
netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java

@@ -10,6 +10,7 @@ import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.MidjourneyPaintingPlazaMapper;
 import com.cyksj.mapper.MidjourneyUserMapper;
 import com.cyksj.model.dto.*;
+import com.cyksj.model.dto.dto.SubmitImagineDTO;
 import com.cyksj.model.entity.GroupsRelation;
 import com.cyksj.model.entity.MidjourneyPaintingPlaza;
 import com.cyksj.model.entity.MidjourneyUser;

+ 0 - 2
netflix-web/src/main/java/com/cyksj/web/controller/mirror/MirrorController.java

@@ -34,14 +34,12 @@ import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.swing.text.html.Option;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.Date;
 import java.util.List;
-import java.util.Optional;
 
 /**
  * /server/镜像服务

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
netflix-web/src/main/resources/application-dev.yml


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
netflix-web/src/main/resources/application-prd.yml


+ 5 - 1
netflix-web/src/main/resources/application-pre.yml

@@ -197,4 +197,8 @@ smsVerify:
 
 midjourney:
   url: https://nf.video
-  drawUrl: https://nf.video/drawTest/web/
+  drawUrl: https://nf.video/drawTest/web/
+
+luma:
+  url: https://luma.galaxydvd.com
+  videoUrl: https://luma.galaxydvd.com/videoTest/web

+ 2 - 1
pom.xml

@@ -132,7 +132,7 @@
             <version>3.8.0</version>
         </dependency>
 
-<!--        <dependency>-->
+        <!--        <dependency>-->
 <!--            <groupId>org.apache.httpcomponents</groupId>-->
 <!--            <artifactId>fluent-hc</artifactId>-->
 <!--            <version>4.5.6</version>-->
@@ -207,6 +207,7 @@
         <module>netflix-dao</module>
         <module>netflix-service</module>
         <module>netflix-web</module>
+        <module>luma</module>
     </modules>
 
 

Някои файлове не бяха показани, защото твърде много файлове са промени