Compare commits
135 Commits
ba2d10afbc
...
dev/3.1_re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d700f94f9d | ||
|
|
b277479e73 | ||
|
|
83cbd57dea | ||
|
|
4d3b22de82 | ||
|
|
6b5c2cfec0 | ||
|
|
b676de054a | ||
|
|
4c169ef67e | ||
|
|
f2bce066b6 | ||
|
|
6af442eb15 | ||
|
|
768df55309 | ||
|
|
f351277b73 | ||
|
|
a799162ea4 | ||
|
|
c035eb9d7d | ||
|
|
906a54b3c8 | ||
|
|
643799546b | ||
|
|
f582464cd3 | ||
|
|
b864b393bc | ||
|
|
c03a8762e7 | ||
|
|
cb87ad1099 | ||
|
|
fb229764f8 | ||
|
|
8bec1f842d | ||
|
|
b54bd04cff | ||
|
|
b4ccad6242 | ||
|
|
6068bf7d7d | ||
|
|
d36baf747f | ||
| 7c8f1bee6a | |||
|
|
62bd145e2c | ||
|
|
23716984cc | ||
|
|
d0b8b8d674 | ||
|
|
92e7dbf258 | ||
|
|
32a228485b | ||
|
|
560e47747a | ||
|
|
c8dc38575a | ||
|
|
c00d906083 | ||
| 4df3f9cc53 | |||
|
|
b0343be544 | ||
|
|
d33cb9f0bf | ||
|
|
40f2735831 | ||
| 01d3806d5f | |||
| 107e4e9771 | |||
|
|
716d720782 | ||
|
|
6b5bacc49b | ||
|
|
409bc7b1fd | ||
|
|
ec6a5df8af | ||
|
|
029b96ae99 | ||
|
|
14002e7331 | ||
| 14dfe2806c | |||
| 798c7b0592 | |||
| 9bd10581f4 | |||
| 1f288fe5e3 | |||
| 72602eb245 | |||
| 983d53268d | |||
| f3aeeb3584 | |||
| 5d3692a204 | |||
| f2a074b2f6 | |||
| 6a7a37dcec | |||
|
|
c4d2780f0e | ||
|
|
1da6b7728c | ||
|
|
0faf77899b | ||
|
|
e4940019bf | ||
|
|
0da66ff210 | ||
|
|
5dd862ff79 | ||
|
|
edaec9884d | ||
|
|
76eeb2be53 | ||
|
|
cb6f94d2d4 | ||
|
|
28656c44c8 | ||
|
|
6757a89d04 | ||
|
|
9be1a1e307 | ||
|
|
2168978f61 | ||
|
|
54466b935d | ||
|
|
c970ebe691 | ||
|
|
1c5a3a12b9 | ||
|
|
6e06000083 | ||
|
|
dea2b3be42 | ||
|
|
bcf51aea23 | ||
|
|
0c9d5404c6 | ||
|
|
93429839c0 | ||
|
|
27859c3e28 | ||
|
|
f02c0930a6 | ||
|
|
d57bb83b25 | ||
| 731e34f133 | |||
| 75eca8d6ba | |||
| 3e53401f76 | |||
|
|
b6a068ebcd | ||
|
|
dc291ea086 | ||
|
|
2e846e671a | ||
|
|
a5093311f9 | ||
|
|
aed338a6d7 | ||
|
|
8bdb49d25c | ||
|
|
5d53a8cd42 | ||
|
|
61b7f3072f | ||
|
|
a1f489f3a1 | ||
|
|
fc3fd877a8 | ||
|
|
fc72d2c430 | ||
|
|
1ac01dd090 | ||
|
|
3bbdf7c672 | ||
|
|
0646484fba | ||
|
|
96b8613741 | ||
|
|
cf30226a51 | ||
|
|
3c15a3ff68 | ||
|
|
0c904be227 | ||
|
|
7759b56123 | ||
|
|
d5bfaa8822 | ||
| 967c0cbc01 | |||
| 417e34b41a | |||
|
|
d51aa84647 | ||
|
|
5895bc6ab6 | ||
|
|
3301869f20 | ||
|
|
1ec42f4ad5 | ||
| cc506ff7e9 | |||
|
|
f2d43f06f4 | ||
|
|
9251df49f8 | ||
| 430156f4e8 | |||
|
|
d1123aedcc | ||
| 8c007077a3 | |||
|
|
d63b4b4e63 | ||
|
|
b826f0bf39 | ||
|
|
1decd8e258 | ||
|
|
1286e84488 | ||
| a252fdf7f9 | |||
| 807d802178 | |||
| 53f1b548be | |||
| 45dd78032a | |||
| c160da5132 | |||
| b23faeeee2 | |||
| 67789abca4 | |||
| 1c78d66aab | |||
| 528bc69923 | |||
|
|
22880d128d | ||
| 9c56a102cc | |||
| 2f59fe074f | |||
| 9c61b1c8fe | |||
| e30fdf7401 | |||
| 6146112d04 | |||
|
|
3e334d7956 |
111
.gitea/workflows/develop_3.1_MS_build_manual.yaml
Normal file
111
.gitea/workflows/develop_3.1_MS_build_manual.yaml
Normal file
@@ -0,0 +1,111 @@
|
||||
name: 手动 AiDA back-java 开发分支构建部署
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: java21
|
||||
|
||||
outputs:
|
||||
build_status: ${{ job.status }}
|
||||
build_url: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/DevelopVersion/develop-MS-version-aida-back
|
||||
|
||||
steps:
|
||||
- name: 0.记录开始时间
|
||||
id: build_start_time
|
||||
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 1.检出代码
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev/3.1_release_merge_MS
|
||||
|
||||
|
||||
- name: 3.缓存 Maven 依赖
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: 4.构建项目
|
||||
run: |
|
||||
java -version
|
||||
mvn -v
|
||||
mvn clean package -DskipTests
|
||||
|
||||
- name: 5.生成Dockerfile
|
||||
run: |
|
||||
echo "===== 生成Dockerfile ====="
|
||||
cat > Dockerfile << 'EOF'
|
||||
FROM openjdk:21-ea-21-jdk-slim
|
||||
VOLUME /tmp
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' > /etc/timezone
|
||||
ADD ./target/aida-0.0.1-SNAPSHOT.jar /app.jar
|
||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
||||
EOF
|
||||
echo "Dockerfile内容:"
|
||||
cat Dockerfile
|
||||
|
||||
- name: 6.生成docker-compose.yml
|
||||
run: |
|
||||
echo "===== 生成docker-compose.yml ====="
|
||||
cat > docker-compose.yml << 'EOF'
|
||||
version: '3'
|
||||
services:
|
||||
aida_back:
|
||||
container_name: develop-aida-ms
|
||||
build: .
|
||||
volumes:
|
||||
# 数据挂载
|
||||
- ./log:/log
|
||||
- ./temp:/temp
|
||||
- ./uploads:/temp/uploads
|
||||
ports:
|
||||
- '10092:10092'
|
||||
restart: always
|
||||
EOF
|
||||
# 验证docker-compose.yml生成
|
||||
echo "docker-compose.yml内容:"
|
||||
cat docker-compose.yml
|
||||
|
||||
- name: 7.上传jar到远程服务器
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
port: 22
|
||||
username: ${{ secrets.SERVER_USER }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
source: "target/*.jar,Dockerfile,docker-compose.yml"
|
||||
target: ${{ env.REMOTE_DEPLOY_PATH }}
|
||||
preserve_host_directory_structure: false
|
||||
|
||||
- name: 8. 重启 Docker 服务
|
||||
uses: appleboy/ssh-action@master # 👈 专门执行命令的 action
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USER }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
key_base64: true
|
||||
script: |
|
||||
echo "========= 进入部署目录 ========="
|
||||
cd ${{ env.REMOTE_DEPLOY_PATH }}
|
||||
ls -l
|
||||
|
||||
echo "========= 停止旧服务 ========="
|
||||
docker compose down
|
||||
|
||||
echo "========= 启动新服务 ========="
|
||||
docker compose up -d --build
|
||||
|
||||
echo "========= 查看运行状态 ========="
|
||||
docker compose ps
|
||||
@@ -99,9 +99,17 @@ jobs:
|
||||
volumes:
|
||||
# 数据挂载
|
||||
- ./log:/log
|
||||
- ./temp:/temp
|
||||
- ./uploads:/temp/uploads
|
||||
ports:
|
||||
- '10090:5567'
|
||||
networks:
|
||||
- aida_java_net
|
||||
restart: always
|
||||
networks:
|
||||
aida_java_net:
|
||||
external: true
|
||||
name: aida_java_net
|
||||
EOF
|
||||
# 验证docker-compose.yml生成
|
||||
echo "docker-compose.yml内容:"
|
||||
|
||||
70
pom.xml
70
pom.xml
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.6</version>
|
||||
<version>3.2.5</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.aida</groupId>
|
||||
@@ -15,7 +15,7 @@
|
||||
<description>ai da</description>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<mybatis.plus.version>3.5.5</mybatis.plus.version>
|
||||
<mybatis.plus.version>3.5.7</mybatis.plus.version>
|
||||
<hutool.version>5.8.23</hutool.version>
|
||||
<wx.java.version>4.2.7.B</wx.java.version>
|
||||
<fastjson.version>2.0.43</fastjson.version>
|
||||
@@ -28,6 +28,11 @@
|
||||
<javacv.version>1.5.5</javacv.version>
|
||||
<system.windowsx64>windows-x86_64</system.windowsx64>
|
||||
<javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>
|
||||
|
||||
<!-- Spring Cloud Alibaba 版本 -->
|
||||
<spring-cloud-alibaba.version>2023.0.3.4</spring-cloud-alibaba.version>
|
||||
<!-- Spring Cloud 版本 -->
|
||||
<spring-cloud.version>2023.0.4</spring-cloud.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -38,6 +43,22 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- Spring Cloud 依赖版本管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- Spring Cloud Alibaba 依赖版本管理 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||
<version>${spring-cloud-alibaba.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -74,9 +95,14 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis-spring</artifactId>
|
||||
<version>3.0.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -237,6 +263,12 @@
|
||||
<version>2.15.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.13.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.stripe</groupId>
|
||||
<artifactId>stripe-java</artifactId>
|
||||
@@ -427,6 +459,38 @@
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
</dependency>
|
||||
<!-- AOP -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ==================== Spring Cloud Alibaba 微服务相关 ==================== -->
|
||||
<!-- 启用 bootstrap.yml 加载 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<!-- Nacos 服务注册与发现 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<!-- Nacos 配置中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<!-- OpenFeign 服务调用 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Cloud LoadBalancer 负载均衡 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package com.ai.da;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
public class AiDaApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AiDaApplication.class, args);
|
||||
log.info("AiDaApplication 启动完成!");
|
||||
}
|
||||
|
||||
}
|
||||
package com.ai.da;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@EnableFeignClients
|
||||
@EnableDiscoveryClient
|
||||
public class AiDaApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AiDaApplication.class, args);
|
||||
log.info("AiDaApplication 启动完成!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -559,83 +559,83 @@ public class GenerateConsumer {
|
||||
log.info("============ProcessPoseTransformResult End listening==========");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer1(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 1");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer2(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 2");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer3(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 3");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer4(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 4");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer5(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 5");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer6(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 6");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer7(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 7");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer8(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 8");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer9(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 9");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generateResult}")
|
||||
@RabbitHandler
|
||||
public void getGenerateResult(Message msg, Channel channel) {
|
||||
processGenerateResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageResult}")
|
||||
@RabbitHandler
|
||||
public void getToProductImageResult(Message msg, Channel channel) {
|
||||
processToProductImageResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.relightResult}")
|
||||
@RabbitHandler
|
||||
public void getRelightResult(Message msg, Channel channel) {
|
||||
processRelightResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransform}")
|
||||
@RabbitHandler
|
||||
public void getPoseTransformationResult(Message msg, Channel channel) {
|
||||
processPoseTransformResult(msg, channel);
|
||||
}
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer1(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 1");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer2(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 2");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer3(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 3");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer4(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 4");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer5(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 5");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer6(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 6");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer7(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 7");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer8(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 8");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
// @RabbitHandler
|
||||
// public void generateConsumer9(Message msg, Channel channel) {
|
||||
// generate(msg, channel, "consumer 9");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.generateResult}")
|
||||
// @RabbitHandler
|
||||
// public void getGenerateResult(Message msg, Channel channel) {
|
||||
// processGenerateResult(msg, channel);
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageResult}")
|
||||
// @RabbitHandler
|
||||
// public void getToProductImageResult(Message msg, Channel channel) {
|
||||
// processToProductImageResult(msg, channel);
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.relightResult}")
|
||||
// @RabbitHandler
|
||||
// public void getRelightResult(Message msg, Channel channel) {
|
||||
// processRelightResult(msg, channel);
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransform}")
|
||||
// @RabbitHandler
|
||||
// public void getPoseTransformationResult(Message msg, Channel channel) {
|
||||
// processPoseTransformResult(msg, channel);
|
||||
// }
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getDesignBatchResult(Message msg, Channel channel) {
|
||||
|
||||
@@ -28,6 +28,11 @@ public class MQPublisher {
|
||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm);
|
||||
}
|
||||
|
||||
public void sendGenerateResultMessage(String mm) {
|
||||
log.info("send generate result message: {}", mm);
|
||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerateResult(), mm);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mailParams 含有的字段
|
||||
|
||||
@@ -222,16 +222,16 @@ public class SRConsumer {
|
||||
taskListService.updateTaskStatusOrOutputRedis(uniqueId, "fail", null);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.sr}")
|
||||
@RabbitHandler
|
||||
public void SRConsumer1(Message msg, Channel channel) {
|
||||
superResolution(msg, channel, "consumer 1");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.srResult}")
|
||||
@RabbitHandler
|
||||
public void SRResultConsumer1(Message msg, Channel channel) {
|
||||
getSRResult(msg, channel, "consumer 1");
|
||||
}
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.sr}")
|
||||
// @RabbitHandler
|
||||
// public void SRConsumer1(Message msg, Channel channel) {
|
||||
// superResolution(msg, channel, "consumer 1");
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.srResult}")
|
||||
// @RabbitHandler
|
||||
// public void SRResultConsumer1(Message msg, Channel channel) {
|
||||
// getSRResult(msg, channel, "consumer 1");
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.ai.da.common.aspect;
|
||||
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller日志切面
|
||||
* 记录所有Controller接口的请求参数和用户信息
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class ControllerLoggingAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ControllerLoggingAspect.class);
|
||||
|
||||
/**
|
||||
* 定义切点:所有Controller方法
|
||||
*/
|
||||
@Pointcut("execution(* com.ai.da.controller..*(..))")
|
||||
public void controllerMethods() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller方法执行前记录日志
|
||||
*/
|
||||
// @Before("controllerMethods()")
|
||||
public void logControllerBefore(JoinPoint joinPoint) {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
// 获取当前用户ID
|
||||
Long userId = null;
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
if (authPrincipalVo != null) {
|
||||
userId = authPrincipalVo.getId();
|
||||
}
|
||||
|
||||
// 获取请求参数
|
||||
Map<String, Object> params = getRequestParams(joinPoint, request);
|
||||
|
||||
logger.info("=== 请求开始 ===");
|
||||
logger.info("用户ID: {}", userId);
|
||||
logger.info("请求URL: {}", request.getRequestURL().toString());
|
||||
logger.info("请求方法: {}", request.getMethod());
|
||||
logger.info("请求IP: {}", getClientIpAddress(request));
|
||||
logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
|
||||
logger.info("请求参数: {}", params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*/
|
||||
private Map<String, Object> getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
// 1. 获取Query String参数
|
||||
String queryString = request.getQueryString();
|
||||
if (queryString != null && !queryString.isEmpty()) {
|
||||
params.put("queryString", queryString);
|
||||
}
|
||||
|
||||
// 2. 获取方法参数(包含 @PathVariable, @RequestParam, @RequestBody 等)
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
if (args != null && args.length > 0) {
|
||||
Map<String, Object> methodParams = new HashMap<>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
// 过滤掉不可序列化的参数
|
||||
if (arg != null) {
|
||||
if (isIgnorable(arg)) {
|
||||
// 对于可忽略的类型,记录类型名
|
||||
methodParams.put("arg" + i, "[" + arg.getClass().getSimpleName() + "]");
|
||||
} else {
|
||||
try {
|
||||
methodParams.put("arg" + i, arg);
|
||||
} catch (Exception e) {
|
||||
methodParams.put("arg" + i, arg.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!methodParams.isEmpty()) {
|
||||
params.put("methodParams", methodParams);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的参数类型
|
||||
*/
|
||||
private boolean isIgnorable(Object obj) {
|
||||
return obj instanceof HttpServletRequest
|
||||
|| obj instanceof HttpServletResponse
|
||||
|| obj instanceof MultipartFile
|
||||
|| obj instanceof MultipartFile[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller方法抛出异常时记录日志
|
||||
*/
|
||||
@AfterThrowing(pointcut = "controllerMethods()", throwing = "exception")
|
||||
public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
Long userId = null;
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
if (authPrincipalVo != null) {
|
||||
userId = authPrincipalVo.getId();
|
||||
}
|
||||
|
||||
// 获取请求参数
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
params = getRequestParams(joinPoint, request);
|
||||
}
|
||||
|
||||
logger.error("=== 请求异常 ===");
|
||||
logger.error("用户ID: {}", userId);
|
||||
logger.error("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
|
||||
logger.error("请求参数: {}", params);
|
||||
logger.error("异常信息: ", exception);
|
||||
logger.error("=== 异常结束 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP地址
|
||||
*/
|
||||
private String getClientIpAddress(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
|
||||
return xForwardedFor.split(",")[0];
|
||||
}
|
||||
|
||||
String xRealIp = request.getHeader("X-Real-IP");
|
||||
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
|
||||
return xRealIp;
|
||||
}
|
||||
|
||||
String proxyClientIp = request.getHeader("Proxy-Client-IP");
|
||||
if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) {
|
||||
return proxyClientIp;
|
||||
}
|
||||
|
||||
String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");
|
||||
if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {
|
||||
return wlProxyClientIp;
|
||||
}
|
||||
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public class MyTaskScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
public void sendTrialOrderExcelToManagements() {
|
||||
// 获取前一天日期
|
||||
LocalDate yesterday = LocalDate.now().minusDays(1);
|
||||
|
||||
34
src/main/java/com/ai/da/common/config/SecurityConfig.java
Normal file
34
src/main/java/com/ai/da/common/config/SecurityConfig.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.ai.da.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Spring Security 配置。
|
||||
* 由于鉴权逻辑已迁移至 Gateway(GlobalAuthWebFilter),
|
||||
* 后端服务 (aida-back) 默认放行所有请求,仅依赖网关传递的用户信息。
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// 禁用 CSRF(微服务通常不需要)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
// 允许所有请求,具体鉴权在网关层完成
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
// 禁用默认的表单登录和 HTTP Basic 认证,防止 302 重定向
|
||||
.formLogin(AbstractHttpConfigurer::disable)
|
||||
.httpBasic(AbstractHttpConfigurer::disable);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.ai.da.common.config;
|
||||
|
||||
|
||||
import com.ai.da.common.interceptor.UserContextInterceptor;
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
@@ -17,11 +20,20 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE"};
|
||||
|
||||
@Resource
|
||||
private UserContextInterceptor userContextInterceptor;
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedOriginPatterns("*").allowCredentials(true).allowedMethods(ORIGINS).maxAge(3600);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(userContextInterceptor)
|
||||
.addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Validator validator() {
|
||||
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
|
||||
|
||||
@@ -1,89 +1,99 @@
|
||||
package com.ai.da.common.config.exception;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 全局异常捕获
|
||||
* @create: 2019-12-03 10:24
|
||||
**/
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
public class ExceptionCatch {
|
||||
|
||||
/**
|
||||
* 线程安全,且构建后不可更改
|
||||
*/
|
||||
private static ImmutableMap<Class<? extends Throwable>, ResultEnum> EXCEPTIONS;
|
||||
|
||||
/**
|
||||
* 用于构建ImmutableMap
|
||||
*/
|
||||
private static ImmutableMap.Builder<Class<? extends Throwable>, ResultEnum> builder = ImmutableMap.builder();
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Response<String> businessExceptionCatch(BusinessException e) {
|
||||
log.error("发生业务异常,code:[{}],msg:[{}]", e.getCode(), e.getMsg(), e);
|
||||
return Response.error(e.getCode(), e.getMsg());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Response<String> exceptionCatch(Exception e) {
|
||||
log.error("发生系统异常,message:[{}]", e.getMessage(), e);
|
||||
//如果ImmutableMap集合为空,构建ImmutableMap
|
||||
if (EXCEPTIONS == null || EXCEPTIONS.size() == 0) {
|
||||
EXCEPTIONS = builder.build();
|
||||
}
|
||||
//获取不可预知异常自定义错误码
|
||||
if (EXCEPTIONS != null) {
|
||||
ResultEnum resultEnum = EXCEPTIONS.get(e.getClass());
|
||||
if (resultEnum != null) {
|
||||
return Response.error(resultEnum.getCode(), resultEnum.getMsg());
|
||||
}
|
||||
}
|
||||
return Response.error(ResultEnum.ERROR.getCode(), e.getMessage() == null ? ResultEnum.ERROR.getMsg() : e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*
|
||||
* @param e
|
||||
* @return ResponseData
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(BindException.class)
|
||||
public Response<String> bindExceptionHandler(BindException e) {
|
||||
log.error("参数错误bind:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
BusinessException businessException = new BusinessException(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
return Response.error(businessException.getCode(), businessException.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*
|
||||
* @param e
|
||||
* @return ResponseData
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Response<String> handleValidationException(MethodArgumentNotValidException e) {
|
||||
log.error("参数错误bind:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
BusinessException businessException = new BusinessException(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
return Response.error(businessException.getCode(), businessException.getMsg());
|
||||
}
|
||||
|
||||
//初始化,不可预知异常自定义错误编码
|
||||
static {
|
||||
// builder.put(FileNotFoundException.class, ResultEnum.FILE_NOT_EXIST);
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.config.exception;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 全局异常捕获
|
||||
* @create: 2019-12-03 10:24
|
||||
**/
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
public class ExceptionCatch {
|
||||
|
||||
/**
|
||||
* 线程安全,且构建后不可更改
|
||||
*/
|
||||
private static ImmutableMap<Class<? extends Throwable>, ResultEnum> EXCEPTIONS;
|
||||
|
||||
/**
|
||||
* 用于构建ImmutableMap
|
||||
*/
|
||||
private static ImmutableMap.Builder<Class<? extends Throwable>, ResultEnum> builder = ImmutableMap.builder();
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Response<String> businessExceptionCatch(BusinessException e) {
|
||||
log.error("发生业务异常,code:[{}],msg:[{}]", e.getCode(), e.getMsg(), e);
|
||||
return Response.error(e.getCode(), e.getMsg());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
@ExceptionHandler(UnauthorizedException.class)
|
||||
public Response<String> unauthorizedExceptionCatch(UnauthorizedException e) {
|
||||
log.error("Unauthorized: {}", e.getMessage());
|
||||
return Response.error(401, e.getMessage());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Response<String> exceptionCatch(Exception e) {
|
||||
log.error("发生系统异常,message:[{}]", e.getMessage(), e);
|
||||
//如果ImmutableMap集合为空,构建ImmutableMap
|
||||
if (EXCEPTIONS == null || EXCEPTIONS.size() == 0) {
|
||||
EXCEPTIONS = builder.build();
|
||||
}
|
||||
//获取不可预知异常自定义错误码
|
||||
if (EXCEPTIONS != null) {
|
||||
ResultEnum resultEnum = EXCEPTIONS.get(e.getClass());
|
||||
if (resultEnum != null) {
|
||||
return Response.error(resultEnum.getCode(), resultEnum.getMsg());
|
||||
}
|
||||
}
|
||||
return Response.error(ResultEnum.ERROR.getCode(), e.getMessage() == null ? ResultEnum.ERROR.getMsg() : e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*
|
||||
* @param e
|
||||
* @return ResponseData
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(BindException.class)
|
||||
public Response<String> bindExceptionHandler(BindException e) {
|
||||
log.error("参数错误bind:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
BusinessException businessException = new BusinessException(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
return Response.error(businessException.getCode(), businessException.getMsg());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*
|
||||
* @param e
|
||||
* @return ResponseData
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Response<String> handleValidationException(MethodArgumentNotValidException e) {
|
||||
log.error("参数错误bind:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
BusinessException businessException = new BusinessException(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||
return Response.error(businessException.getCode(), businessException.getMsg());
|
||||
}
|
||||
|
||||
//初始化,不可预知异常自定义错误编码
|
||||
static {
|
||||
// builder.put(FileNotFoundException.class, ResultEnum.FILE_NOT_EXIST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.ai.da.common.config.exception;
|
||||
|
||||
public class TokenMissingOrExpiredException extends RuntimeException {
|
||||
public TokenMissingOrExpiredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ai.da.common.config.exception;
|
||||
|
||||
public class UnauthorizedException extends RuntimeException {
|
||||
|
||||
public UnauthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnauthorizedException() {
|
||||
super("Gateway token verification failed");
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public class CommonConstant {
|
||||
// 单位 秒 两天过期
|
||||
public static final Long CREDITS_EXPIRE_TIME = 2 * 24 * 60 * 60L;
|
||||
// 单位 分钟
|
||||
public static final Integer MINIO_IMAGE_EXPIRE_TIME = 24 * 60;
|
||||
public static final Integer MINIO_IMAGE_EXPIRE_TIME = 24 * 60 * 7;
|
||||
// 单位 秒 一天过期 in redis
|
||||
public static final Long GENERATE_RESULT_EXPIRE_TIME = 24 * 60 * 60L;
|
||||
// 单位 秒 7天过期
|
||||
@@ -23,6 +23,7 @@ public class CommonConstant {
|
||||
}
|
||||
|
||||
public static final String GENERATE_PATH = "/api/generate_image";
|
||||
public static final String GENERATE_PATH_FLUX2_KLEIN = "/api/generate_image_flux2_klein";
|
||||
|
||||
public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo";
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ public class ModelConstants {
|
||||
|
||||
// 模型名称常量
|
||||
public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image";
|
||||
public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415";
|
||||
public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415";
|
||||
public static final String PRINTBOARD_HIGH_I2I = "doubao-seededit-3-0-i2i-250628";
|
||||
public static final String MOODBOARD_ADVANCED = "doubao-seedream-4-5-251128";
|
||||
public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-4-0-250828-high";
|
||||
public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828-fast";
|
||||
public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828";
|
||||
public static final String IMAGEN_MODEL = "imagen-4.0-generate-001";
|
||||
public static final String NANO_BANANA = "gemini-2.5-flash-image";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.ai.da.common.constant;
|
||||
|
||||
/**
|
||||
* @author yanglei
|
||||
* 异常类常量
|
||||
*/
|
||||
public class TokenConstant {
|
||||
/**
|
||||
* 固定session
|
||||
*/
|
||||
public static final String FIX_SESSION = "qrLS_003af9d8c1363fc4_6c97e932665c4460a1fdbfbf47ce3490";
|
||||
|
||||
public static final String PERMISSIONS = "9672233956";
|
||||
}
|
||||
@@ -1,19 +1,41 @@
|
||||
package com.ai.da.common.context;
|
||||
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
|
||||
public class UserContext {
|
||||
private static ThreadLocal<AuthPrincipalVo> userHolder = new ThreadLocal<AuthPrincipalVo>();
|
||||
|
||||
public static AuthPrincipalVo getUserHolder() {
|
||||
return userHolder.get();
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
userHolder.remove();
|
||||
}
|
||||
|
||||
public static void setUserHolder(AuthPrincipalVo authPrincipalVo) {
|
||||
userHolder.set(authPrincipalVo);
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.context;
|
||||
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
|
||||
public class UserContext {
|
||||
private static final ThreadLocal<AuthPrincipalVo> userHolder = new ThreadLocal<>();
|
||||
|
||||
public static void setUserHolder(AuthPrincipalVo authPrincipalVo) {
|
||||
userHolder.set(authPrincipalVo);
|
||||
}
|
||||
|
||||
public static AuthPrincipalVo getUserHolder() {
|
||||
AuthPrincipalVo holder = userHolder.get();
|
||||
if (holder == null) {
|
||||
throw new RuntimeException("User not authenticated");
|
||||
}
|
||||
if (!"AIDA".equals(holder.getSource())) {
|
||||
throw new RuntimeException("Access denied: source must be AIDA");
|
||||
}
|
||||
return holder;
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
userHolder.remove();
|
||||
}
|
||||
|
||||
public static Long getUserId() {
|
||||
return getUserHolder().getId();
|
||||
}
|
||||
|
||||
public static Long getBuyerId() {
|
||||
AuthPrincipalVo holder = userHolder.get();
|
||||
if (holder == null) {
|
||||
throw new RuntimeException("User not authenticated");
|
||||
}
|
||||
if (!"BUYER".equals(holder.getSource())) {
|
||||
throw new RuntimeException("Access denied: source must be BUYER");
|
||||
}
|
||||
return holder.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.ai.da.common.httpdata.token;
|
||||
|
||||
public enum TokenApis {
|
||||
/**
|
||||
* token
|
||||
*/
|
||||
GET_TOKEN("POST", "/api/openApi/v2/Weixin/QrCodeLoginCheck?session="),
|
||||
GENERATE_USER("POST", "/api/openApi/v2/Welink/TopicGetjson");
|
||||
|
||||
private String method;
|
||||
private String url;
|
||||
|
||||
TokenApis(String method, String url) {
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ai.da.common.httpdata.token;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class TokenQuery {
|
||||
|
||||
private static final String GET_TOKEN_DOMAIN = "https://www.szsige.com";
|
||||
private static final String GENERATE_USER_DOMAIN = "https://www.szsige.com";
|
||||
|
||||
public static JSONObject getToken(String session) {
|
||||
String url = GET_TOKEN_DOMAIN + TokenApis.GET_TOKEN.getUrl() + session;
|
||||
log.info("获取用户token接口请求url:" + url);
|
||||
HttpResponse httpResponse = HttpUtil.createPost(url).execute();
|
||||
log.info("获取用户token接口响应:" + httpResponse);
|
||||
if (httpResponse.isOk() && StrUtil.isNotEmpty(httpResponse.body())) {
|
||||
return JSONObject.parseObject(httpResponse.body());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JSONObject generateUser(Map<String, Object> param, String token) {
|
||||
HttpResponse httpResponse = HttpUtil.createPost(GENERATE_USER_DOMAIN + TokenApis.GENERATE_USER.getUrl())
|
||||
.body(JSONObject.toJSONString(param != null ? param : new HashMap<>()))
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("X-Promiss", "9672233956")
|
||||
.execute();
|
||||
log.info("生成用户信息接口响应:" + httpResponse);
|
||||
if (httpResponse.isOk() && StrUtil.isNotEmpty(httpResponse.body())) {
|
||||
return JSONObject.parseObject(httpResponse.body());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.ai.da.common.interceptor;
|
||||
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 从 Gateway 转发的请求头中读取已鉴权的用户身份,写入 UserContext。
|
||||
* <p>
|
||||
* Gateway 验证 JWT 后将 X-User-Id 和 X-User-Info 写入请求头,
|
||||
* 此拦截器负责将信息填充到 ThreadLocal,供业务代码使用。
|
||||
* 不需要 Gateway 鉴权路径(如 login、静态资源)不会有这两个头,
|
||||
* 此时 UserContext 保持为空,业务代码应自行处理。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class UserContextInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final String USER_ID_HEADER = "X-User-Id";
|
||||
private static final String USER_INFO_HEADER = "X-User-Info";
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
String userInfoJson = request.getHeader(USER_INFO_HEADER);
|
||||
if (userInfoJson != null && !userInfoJson.isBlank()) {
|
||||
try {
|
||||
AuthPrincipalVo principal = objectMapper.readValue(userInfoJson, AuthPrincipalVo.class);
|
||||
UserContext.setUserHolder(principal);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse X-User-Info header: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
UserContext.delete();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.common.utils.JSONResponseUtils;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName UserAuthAccessDeniedHandler
|
||||
* @Description 无权限处理类
|
||||
* @Author dwjian
|
||||
* @Date 2020/7/9 20:30
|
||||
*/
|
||||
@Component
|
||||
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler {
|
||||
@Override
|
||||
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException exception) throws IOException {
|
||||
Response<String> response = Response.error(ResultEnum.NO_PERMISSION.getCode(), ResultEnum.NO_PERMISSION.getMsg());
|
||||
JSONResponseUtils.build(httpServletResponse, response);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.common.utils.JSONResponseUtils;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName UserAuthenticationEntryPointHandler
|
||||
* @Description 未登录处理类
|
||||
* @Author dwjian
|
||||
* @Date 2020/7/9 20:13
|
||||
*/
|
||||
@Component
|
||||
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {
|
||||
@Override
|
||||
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
|
||||
Response<String> response = Response.error(ResultEnum.NO_LOGIN.getCode(), ResultEnum.NO_LOGIN.getMsg());
|
||||
httpServletResponse.setStatus(401);
|
||||
JSONResponseUtils.build(httpServletResponse, response);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证管理器
|
||||
* @create: 2020-07-10 15:58
|
||||
**/
|
||||
@Component
|
||||
public class UserAuthenticationManager implements AuthenticationManager {
|
||||
|
||||
@Resource
|
||||
private UserAuthenticationProvider userAuthenticationProvider;
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
return userAuthenticationProvider.authenticate(authentication);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import com.ai.da.common.config.RsaProperties;
|
||||
import com.ai.da.common.utils.RsaDecryptUtils;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 登录校验处理类
|
||||
* @create: 2020-07-09 14:39
|
||||
**/
|
||||
@Component
|
||||
public class UserAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
String userName = (String) authentication.getPrincipal();
|
||||
String password = (String) authentication.getCredentials();
|
||||
try {
|
||||
password = RsaDecryptUtils.decrypt(password, RsaProperties.privateKey);
|
||||
} catch (Exception e) {
|
||||
throw new BadCredentialsException("用户名或密码错误");
|
||||
}
|
||||
// User user = userService.getByUsername(userName);
|
||||
// if (user == null) {
|
||||
// throw new UsernameNotFoundException("用户名或密码错误");
|
||||
// }
|
||||
// //账号已冻结
|
||||
// if(user.getStatus() == 1){
|
||||
// throw new LockedException("账号已冻结");
|
||||
// }
|
||||
// if(!passwordEncoder.matches(password, user.getPassword())){
|
||||
// throw new BadCredentialsException("用户名或密码错误");
|
||||
// }
|
||||
// //超级管理员
|
||||
// Set<SimpleGrantedAuthority> authorities = new HashSet<>();
|
||||
// if(user.getIsAdmin()) {
|
||||
// authorities.add(new SimpleGrantedAuthority("admin"));
|
||||
// return new UsernamePasswordAuthenticationToken(user, password, authorities);
|
||||
// }else {
|
||||
// List<RoleMenuDto> userMenus = menuService.getRoleMenusByUserId(user.getId(), null);
|
||||
// if(CollUtil.isNotEmpty(userMenus)){
|
||||
// authorities.addAll(userMenus.stream().map(RoleMenuDto::getPermission).filter(StringUtils::isNotEmpty).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
|
||||
// }
|
||||
// return new UsernamePasswordAuthenticationToken(user, password, authorities);
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> aClass) {
|
||||
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.common.utils.JSONResponseUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @ClassName UserLoginFailureHandler
|
||||
* @Description 登录失败处理类
|
||||
* @Author dwjian
|
||||
* @Date 2020/7/9 20:17
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
|
||||
Response<String> response;
|
||||
if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
|
||||
response = Response.fail(e.getMessage());
|
||||
} else if (e instanceof LockedException) {
|
||||
response = Response.fail(ResultEnum.ACCOUNT_LOCK);
|
||||
} else if (e instanceof CredentialsExpiredException) {
|
||||
response = Response.fail("证书过期,请联系管理员!");
|
||||
} else if (e instanceof AccountExpiredException) {
|
||||
response = Response.fail("账户过期,请联系管理员!");
|
||||
} else if (e instanceof DisabledException) {
|
||||
response = Response.fail("账户被禁用,请联系管理员!");
|
||||
} else if (e instanceof AuthenticationServiceException) {
|
||||
response = Response.fail(e.getMessage());
|
||||
} else {
|
||||
log.error("登录失败:", e);
|
||||
response = Response.fail("登录失败!");
|
||||
}
|
||||
JSONResponseUtils.build(httpServletResponse, response);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.common.utils.JSONResponseUtils;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 登录成功处理类
|
||||
* @create: 2020-07-09 14:58
|
||||
**/
|
||||
@Component
|
||||
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
|
||||
// User user = (User) authentication.getPrincipal();
|
||||
// AuthPrincipalVo principal = new AuthPrincipalVo();
|
||||
// BeanUtils.copyProperties(user, principal);
|
||||
// // 获取用户角色
|
||||
// List<UserRoleDto> userRoles = roleService.getUserRoles(user.getId(), 0);
|
||||
// principal.setRoles(userRoles);
|
||||
// // 获取角色部门
|
||||
// if(CollUtil.isNotEmpty(userRoles)){
|
||||
// principal.setDepts(deptService.getDeptByRoleIds(userRoles.stream().map(UserRoleDto::getRoleId).collect(Collectors.toList())));
|
||||
// }
|
||||
// // 用户角色权限
|
||||
// List<String> authorities = new ArrayList<>(authentication.getAuthorities()).stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
|
||||
// principal.setAuthorities(authorities);
|
||||
// AuthVo authVo = new AuthVo();
|
||||
// authVo.setAuthorities(authorities);
|
||||
// authVo.setToken(jwtTokenHelper.createToken(principal));
|
||||
// authVo.setPrincipal(principal);
|
||||
// user.setLastLoginTime(new Date());
|
||||
// userService.updateById(user);
|
||||
JSONResponseUtils.build(response, Response.success(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), null));
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.ai.da.common.security;
|
||||
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @ClassName UserPermissionEvaluator
|
||||
* @Description 权限校验处理器
|
||||
* @Author dwjian
|
||||
* @Date 2020/7/12 10:16
|
||||
*/
|
||||
@Component
|
||||
public class UserPermissionEvaluator implements PermissionEvaluator {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
|
||||
System.out.println(authentication);
|
||||
System.out.println(targetUrl);
|
||||
System.out.println(permission);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
|
||||
System.out.println(authentication);
|
||||
System.out.println(targetId);
|
||||
System.out.println(targetType);
|
||||
System.out.println(permission);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.ai.da.common.security.config;
|
||||
|
||||
import com.ai.da.common.security.*;
|
||||
import com.ai.da.common.security.filter.AuthenticationFilter;
|
||||
import com.ai.da.common.security.filter.UserAuthenticationProcessingFilter;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(prePostEnabled = true)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class SecurityConfig {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
@Resource
|
||||
private UserLoginSuccessHandler userLoginSuccessHandler;
|
||||
@Resource
|
||||
private UserLoginFailureHandler userLoginFailureHandler;
|
||||
@Resource
|
||||
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
|
||||
@Resource
|
||||
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
|
||||
@Resource
|
||||
private UserAuthenticationManager userAuthenticationManager;
|
||||
@Resource
|
||||
private UserAuthenticationProcessingFilter userAuthenticationProcessingFilter;
|
||||
|
||||
/**
|
||||
* 不通过注入spring管理 让Security来管理 这样自定义的Filter就不会走,.permitAll()才能起作用
|
||||
*/
|
||||
@Resource
|
||||
private AuthenticationFilter authenticationFilter;
|
||||
@Resource
|
||||
private UserPermissionEvaluator userPermissionEvaluator;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return this.userAuthenticationManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity
|
||||
.cors(Customizer.withDefaults())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(
|
||||
new AntPathRequestMatcher("/doc.html"),
|
||||
new AntPathRequestMatcher("/swagger-ui.html"),
|
||||
new AntPathRequestMatcher("/swagger-ui/**"),
|
||||
new AntPathRequestMatcher("/swagger-resources/**"),
|
||||
new AntPathRequestMatcher("/v2/api-docs"),
|
||||
new AntPathRequestMatcher("/v3/api-docs/**"),
|
||||
new AntPathRequestMatcher("/webjars/**")
|
||||
).permitAll()
|
||||
.requestMatchers(securityProperties.getIgnorePaths()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.headers(headers -> headers
|
||||
.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
|
||||
.cacheControl(cache -> cache.disable())
|
||||
)
|
||||
.exceptionHandling(exception -> exception
|
||||
.authenticationEntryPoint(userAuthenticationEntryPointHandler)
|
||||
.accessDeniedHandler(userAuthAccessDeniedHandler)
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginProcessingUrl(securityProperties.getAuthApi())
|
||||
.successHandler(userLoginSuccessHandler)
|
||||
.failureHandler(userLoginFailureHandler)
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
);
|
||||
|
||||
//自定义过滤器在登录时认证用户名、密码
|
||||
httpSecurity.addFilterAt(userAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class);
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
|
||||
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
|
||||
handler.setPermissionEvaluator(userPermissionEvaluator);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.ai.da.common.security.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: JWT配置类
|
||||
* @create: 2020-07-09 09:38
|
||||
**/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "spring.security")
|
||||
public class SecurityProperties {
|
||||
|
||||
private String jwtSecret;
|
||||
|
||||
private String jwtTokenHeader;
|
||||
|
||||
private String jwtTokenPrefix;
|
||||
|
||||
private long jwtExpiration;
|
||||
|
||||
private String[] ignorePaths;
|
||||
|
||||
private String authApi;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.config.exception.TokenMissingOrExpiredException;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.LocalCacheUtils;
|
||||
import com.ai.da.common.utils.RedisUtil;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||
import com.ai.da.common.utils.RequestInfoUtil;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证拦截器
|
||||
* @create: 2020-07-10 16:50
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
@Resource
|
||||
private SecurityProperties properties;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
private static final List<String> FILTER_URL =
|
||||
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
|
||||
"/swagger-resources", "/swagger-resources/", "/swagger-resources/configuration/ui", "/swagger-resources/configuration/security",
|
||||
"/webjars/", "/v2/api-docs", "/v3/api-docs", "/v3/api-docs/swagger-config",
|
||||
"/api/account/login", "/api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
|
||||
"/api/account/resetPwd",
|
||||
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
|
||||
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
|
||||
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew","/api/third/party/updateNoLoginRequiredNew",
|
||||
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
|
||||
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
|
||||
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
||||
"/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
|
||||
, "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
|
||||
"/api/llm/stream",
|
||||
//GlobalAwardController
|
||||
"/api/global-award/uploads/pdf/init", "/api/global-award/uploads/pdf/chunk", "/api/global-award/uploads/pdf/complete", "/api/global-award/uploads/pdf/status",
|
||||
"/api/global-award/uploads/video/init", "/api/global-award/uploads/video/chunk", "/api/global-award/uploads/video/complete", "/api/global-award/uploads/video/status",
|
||||
"/api/global-award/contestants/save", "/api/global-award/contestants/by-email", "/api/global-award/checkEmail", "/api/global-award/checkCode"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestURI = httpServletRequest.getRequestURI();
|
||||
|
||||
if (calculateUrl(requestURI)/* || hasAuthorizationToken(httpServletRequest)*/) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
HttpServletRequest wrappedRequest = httpServletRequest;
|
||||
HttpServletResponse wrappedResponse = httpServletResponse;
|
||||
try {
|
||||
stopWatch.start();
|
||||
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} else {
|
||||
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
|
||||
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
|
||||
extracted(wrappedRequest);
|
||||
// excel导出使用原始response,不对响应做包装
|
||||
if (requestURI.equals("/api/account/exportAccountsToExcel")) {
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse); // 不包装
|
||||
} else {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SecurityContextHolder.clearContext();
|
||||
throw e;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
} else {
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean calculateUrl(String requestURI) {
|
||||
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
|
||||
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
private boolean hasAuthorizationToken(HttpServletRequest request) {
|
||||
String authorizationHeader = request.getHeader("Authorization");
|
||||
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
|
||||
}
|
||||
|
||||
private void extracted(HttpServletRequest request) {
|
||||
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
|
||||
// log.debug("后台检查令牌:{}", jwtToken);
|
||||
|
||||
if (StrUtil.isBlank(jwtToken)) {
|
||||
String ipAddress = RequestInfoUtil.getIpAddress(request);
|
||||
log.info("本次请求的ip为 : " + ipAddress);
|
||||
// throw new RuntimeException("请传入token!");
|
||||
throw new TokenMissingOrExpiredException("请传入token!");
|
||||
}
|
||||
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
|
||||
//写死 暂时放行
|
||||
return;
|
||||
}
|
||||
// 检查token
|
||||
boolean validate = jwtTokenHelper.validateToken(jwtToken);
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
|
||||
if (principal == null) {
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(token without userInfo)");
|
||||
}
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
//存取用户信息到缓存
|
||||
UserContext.setUserHolder(principal);
|
||||
// 校验 token:先查本地缓存,再查 Redis,保证服务重启后仍然有效
|
||||
String userIdStr = String.valueOf(principal.getId());
|
||||
String cacheToken = LocalCacheUtils.getTokenCache(userIdStr);
|
||||
|
||||
if (StringUtils.isEmpty(cacheToken)) {
|
||||
// 本地缓存为空时,尝试从 Redis 读取
|
||||
cacheToken = redisUtil.getLoginToken(principal.getId());
|
||||
if (StringUtils.isEmpty(cacheToken)) {
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(cache & redis empty)");
|
||||
}
|
||||
// 将 Redis 中的 token 回填到本地缓存,减少后续 Redis 访问
|
||||
LocalCacheUtils.setTokenCache(userIdStr, cacheToken);
|
||||
}
|
||||
if(!cacheToken.equals(jwtToken) ){
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(token not match local cache)");
|
||||
}
|
||||
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.security.UserLoginSuccessHandler;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.utils.RedisCacheUtils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ai.da.common.security.UserAuthenticationManager;
|
||||
import com.ai.da.common.security.UserLoginFailureHandler;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 用户认证过滤器
|
||||
* @create: 2020-07-10 15:58
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
/**
|
||||
* @param securityProperties 配置(从配置中读取登录url)
|
||||
* @param authenticationManager 认证管理器
|
||||
* @param adminAuthenticationSuccessHandler 认证成功处理器
|
||||
* @param adminAuthenticationFailureHandler 认证失败处理器
|
||||
*/
|
||||
public UserAuthenticationProcessingFilter(SecurityProperties securityProperties, UserAuthenticationManager authenticationManager, UserLoginSuccessHandler adminAuthenticationSuccessHandler, UserLoginFailureHandler adminAuthenticationFailureHandler) {
|
||||
super(new AntPathRequestMatcher(securityProperties.getAuthApi(), HttpMethod.POST.name()));
|
||||
this.setAuthenticationManager(authenticationManager);
|
||||
this.setAuthenticationSuccessHandler(adminAuthenticationSuccessHandler);
|
||||
this.setAuthenticationFailureHandler(adminAuthenticationFailureHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (request.getContentType() == null || !request.getContentType().contains("application/json")) {
|
||||
throw new AuthenticationServiceException("请求头类型不支持: " + request.getContentType());
|
||||
}
|
||||
UsernamePasswordAuthenticationToken authRequest;
|
||||
try {
|
||||
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
|
||||
// 将前端传递的数据转换成jsonBean数据格式
|
||||
JSONObject jsonObject = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest));
|
||||
String code = jsonObject.getString("code");
|
||||
String uuid = jsonObject.getString("uuid");
|
||||
if (StrUtil.isEmpty(code) || StrUtil.isEmpty(uuid) || !code.equals(RedisCacheUtils.get("code-key-" + uuid, String.class))) {
|
||||
throw new AuthenticationServiceException("验证码错误");
|
||||
}
|
||||
RedisCacheUtils.delete("code-key-" + uuid);
|
||||
authRequest = new UsernamePasswordAuthenticationToken(jsonObject.get("username"), jsonObject.get("password"), null);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(wrappedRequest));
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationServiceException(e.getMessage());
|
||||
}
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.ai.da.common.security.jwt;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: JWT工具
|
||||
* @create: 2020-07-09 09:27
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JWTTokenHelper {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
private static final String ISSUER = "DWJ";
|
||||
private static final String AUTHORITIES = "authorities";
|
||||
private static final String CHANGE_MAILBOX = "changeMailbox";
|
||||
|
||||
public String createToken(AuthPrincipalVo principal) {
|
||||
SecretKey key = buildSigningKey();
|
||||
String token = Jwts.builder()
|
||||
.id(String.valueOf(principal.getId()))
|
||||
.subject(JSONObject.toJSONString(principal))
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.claim(AUTHORITIES, JSON.toJSONString(new ArrayList<>()))//自定义属性 权限
|
||||
.expiration(new Date(System.currentTimeMillis() + securityProperties.getJwtExpiration()))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
token = securityProperties.getJwtTokenPrefix() + token;
|
||||
return token;
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
Claims claims = parser(token);
|
||||
if (MapUtil.isEmpty(claims)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public AuthPrincipalVo parserToUser(String token) {
|
||||
String subject = parser(token).getSubject();
|
||||
if (StrUtil.isNotEmpty(subject)) {
|
||||
return JSONObject.parseObject(subject, AuthPrincipalVo.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Claims parser(String token) {
|
||||
token = token.replaceAll(securityProperties.getJwtTokenPrefix(), "");
|
||||
SecretKey key = buildSigningKey();
|
||||
return Jwts.parser()
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
public String createToken(Long userId, String userEmail){
|
||||
SecretKey key = buildSigningKey();
|
||||
String token = Jwts.builder()
|
||||
.id(String.valueOf(userId))
|
||||
.subject(userEmail + "_" + userId)
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.claim(CHANGE_MAILBOX, JSON.toJSONString(new ArrayList<>()))//自定义属性 权限
|
||||
.expiration(new Date(System.currentTimeMillis() + CommonConstant.CHANGE_MAILBOX_LINK_VALIDITY))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
return token;
|
||||
}
|
||||
|
||||
public String parseToEmailAndId(String token) {
|
||||
return parser(token).getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 要求 HMAC-SHA 的密钥至少 256 bit,这里统一扩展/哈希密钥长度避免 WeakKeyException。
|
||||
*/
|
||||
private SecretKey buildSigningKey() {
|
||||
byte[] raw = securityProperties.getJwtSecret().getBytes(StandardCharsets.UTF_8);
|
||||
if (raw.length < 32) {
|
||||
raw = DigestUtil.sha256(raw);
|
||||
}
|
||||
return Keys.hmacShaKeyFor(raw);
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,13 @@ public class AccountTask {
|
||||
* 每个月月初只刷新教育子账号的积分
|
||||
*/
|
||||
// @Scheduled(cron = "0 25 14 * * ?")
|
||||
@Scheduled(cron = "0 0 0 1 * ?")
|
||||
// @Scheduled(cron = "0 0 0 1 * ?")
|
||||
public void refreshCreditsMonthly() {
|
||||
log.info("每月1号0点 重置教育版子账号为默认积分");
|
||||
accountService.refreshCreditsMonthly();
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void getPaidUser() {
|
||||
// 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单
|
||||
accountService.extendValidityForCC();
|
||||
@@ -54,7 +54,7 @@ public class AccountTask {
|
||||
}*/
|
||||
|
||||
// 每天检测正式用户到期情况,每天凌晨0点执行
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
// @Scheduled(cron = "0 0 0 * * ?")
|
||||
public void paidUserToVisitor() {
|
||||
// 1、查询当前已过期正式用户或试用用户
|
||||
List<Account> accountList = accountService.getExpiredUserBySystemUser(1);
|
||||
@@ -77,7 +77,7 @@ public class AccountTask {
|
||||
accountService.registerUserToVisitor();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 1 * ?")
|
||||
// @Scheduled(cron = "0 0 0 1 * ?")
|
||||
// 每月初刷新所有用户用户名剩余修改次数
|
||||
public void resetUsernameModifyTimes(){
|
||||
log.info("重置所有用户的用户名修改次数");
|
||||
@@ -85,17 +85,17 @@ public class AccountTask {
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 35 14 * * ?")
|
||||
@Scheduled(cron = "0 5 0 * * ?")
|
||||
// @Scheduled(cron = "0 5 0 * * ?")
|
||||
public void checkEduAdminExpireStatus() {
|
||||
accountService.checkEduAdminExpireStatus();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 5 0 * * ?")
|
||||
// @Scheduled(cron = "0 5 0 * * ?")
|
||||
public void activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void expireSubscription() {
|
||||
subscriptionPlanService.expireSubscription();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class GenerateTask {
|
||||
* 故这里通过定时任务做补偿
|
||||
* flux五分钟查询一次,万相1小时查询一次
|
||||
*/
|
||||
@Scheduled(cron = "0 */4 * * * ?")
|
||||
// @Scheduled(cron = "0 */4 * * * ?")
|
||||
public void fluxCompensationMechanism(){
|
||||
// 1、查所有 任务还没成功、还没失败,正在等待或者执行中的任务id有哪些
|
||||
// (由于获取结果的polling_url在redis中只存一天,大部分结果超过一天之后就无法再找到任务,小部分可以通过公共路径查到结果)
|
||||
@@ -98,7 +98,7 @@ public class GenerateTask {
|
||||
}
|
||||
|
||||
// 万相 -> pose transformation 补偿 当前任务执行完后,5分钟再执行一次(不会出现任务重叠的情况)
|
||||
@Scheduled(fixedDelay = 5 * 60 * 1000)
|
||||
// @Scheduled(fixedDelay = 5 * 60 * 1000)
|
||||
public void wxCompensationMechanism(){
|
||||
List<APIGenerate> apiGenerates = apiGenerateService.getPendingTaskByStatus("wx");
|
||||
if (apiGenerates != null && !apiGenerates.isEmpty()){
|
||||
|
||||
@@ -45,7 +45,7 @@ public class PaymentTask {
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirmForPaypal() throws SerializeException {
|
||||
|
||||
// log.info("PayPal orderConfirm 被执行......");
|
||||
@@ -97,19 +97,19 @@ public class PaymentTask {
|
||||
//
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void updateAffiliateInfoWithPayment(){
|
||||
// log.info("佣金计算定时器");
|
||||
affiliateService.updateAffiliateInfoWithPayment();
|
||||
}
|
||||
|
||||
// 定时同步(每分钟一次)
|
||||
@Scheduled(fixedRate = 60000)
|
||||
// @Scheduled(fixedRate = 60000)
|
||||
public void syncLinkViewCountToDB(){
|
||||
affiliateService.syncLinkViewCountToDB();
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 8 28-31 * ?")
|
||||
// @Scheduled(cron = "0 0 8 28-31 * ?")
|
||||
public void commissionSummaryReminder(){
|
||||
// 每个月末的最后一天的早上八点执行
|
||||
LocalDate today = LocalDate.now();
|
||||
@@ -120,7 +120,7 @@ public class PaymentTask {
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void calcCouponsCommission(){
|
||||
// log.info("优惠券佣金计算定时器");
|
||||
affiliateService.calcCouponsCommission();
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SubscriptionReminderTask {
|
||||
REMINDER_DAYS_CONFIG.put("year", 14);
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
public void subscriptionReminder() {
|
||||
// 获取所有需要通知的订阅
|
||||
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
|
||||
@@ -97,7 +97,7 @@ public class SubscriptionReminderTask {
|
||||
return subscriptionInfoMapper.selectList(qw);
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
public void trialReminder() {
|
||||
// 今天的 00:00:00 和 23:59:59
|
||||
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
||||
|
||||
@@ -1,178 +1,171 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.model.dto.BasicEmailParamDTO;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.sun.mail.smtp.SMTPTransport;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MailUtil {
|
||||
|
||||
@Resource
|
||||
private JavaMailSender javaMailSender;
|
||||
|
||||
@Resource
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
/**
|
||||
* 发送邮件 - 默认发件人
|
||||
*
|
||||
* @param basicEmailParamDTO 发送邮件所需参数
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
*/
|
||||
public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource);
|
||||
// 提取配置
|
||||
String host;
|
||||
String username;
|
||||
String password;
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
host = ((JavaMailSenderImpl) javaMailSender).getHost();
|
||||
} else {
|
||||
host = basicEmailParamDTO.getServiceAddress();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) {
|
||||
username = ((JavaMailSenderImpl) javaMailSender).getUsername();
|
||||
} else {
|
||||
username = basicEmailParamDTO.getSenderUser();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
password = ((JavaMailSenderImpl) javaMailSender).getPassword();
|
||||
} else {
|
||||
password = basicEmailParamDTO.getPassword();
|
||||
}
|
||||
return sendMail(mimeMessage, host, username, password);
|
||||
}
|
||||
|
||||
private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException {
|
||||
SMTPTransport transport = null;
|
||||
try {
|
||||
// 获取 SMTPTransport
|
||||
transport = (SMTPTransport) mimeMessage.getSession().getTransport("smtp");
|
||||
// 连接到 SMTP 服务器
|
||||
transport.connect(host, username, password);
|
||||
// 发送邮件
|
||||
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
|
||||
// 获取 SMTP 服务器的响应
|
||||
String lastServerResponse = transport.getLastServerResponse();
|
||||
int lastReturnCode = transport.getLastReturnCode();
|
||||
|
||||
log.info("SMTP 状态码: {}, SMTP 服务器响应: {}", lastReturnCode, lastServerResponse);
|
||||
return lastReturnCode;
|
||||
} catch (MailException | MessagingException e) {
|
||||
// 记录日志或执行其他补偿逻辑
|
||||
log.info("邮件发送失败:{}", e.getMessage());
|
||||
} finally {
|
||||
// 关闭连接
|
||||
assert transport != null;
|
||||
transport.close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一封邮件
|
||||
*
|
||||
* @param basicEmailParamDTO 创建邮件需要的参数
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
* @return 一封邮件
|
||||
*/
|
||||
private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
// 创建邮件对象
|
||||
MimeMessage message = javaMailSender.createMimeMessage();
|
||||
// 使用 MimeMessageHelper 简化邮件内容和附件的设置
|
||||
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
|
||||
// 设置发件人
|
||||
mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail()));
|
||||
// 设置收件人
|
||||
mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo());
|
||||
// 设置抄送人
|
||||
if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) {
|
||||
mimeMessageHelper.setCc(basicEmailParamDTO.getCc());
|
||||
}
|
||||
// 设置暗送人
|
||||
if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) {
|
||||
mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc());
|
||||
}
|
||||
// 设置邮件主题
|
||||
mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject());
|
||||
// 设置邮件内容(HTML 格式)
|
||||
mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true);
|
||||
// 设置附件
|
||||
if (inputStreamSource != null) {
|
||||
mimeMessageHelper.addAttachment(fileName, inputStreamSource);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置实体参数
|
||||
*
|
||||
* @param mailTo 接收邮件的邮箱地址
|
||||
* @param jsonObject 模板中变量的值
|
||||
* @return 返回一个MailEntity
|
||||
* @throws AddressException 邮箱地址值异常
|
||||
*/
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
// basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setSenderUserMail(CommonConstant.senderEmail);
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
// todo 邮件模板不存在的报错与重试机制
|
||||
basicEmailParamDTO.setContent(setContent(jsonObject, templatePath));
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地址转换为InternetAddress类型
|
||||
*
|
||||
* @param addressList 普通的地址字符串列表
|
||||
* @return InternetAddress类型的地址列表
|
||||
* @throws AddressException 地址异常
|
||||
*/
|
||||
public InternetAddress[] getInternetAddressList(List<String> addressList) throws AddressException {
|
||||
InternetAddress[] toAddress = new InternetAddress[addressList.size()];
|
||||
for (String address : addressList) {
|
||||
toAddress[addressList.indexOf(address)] = new InternetAddress(address);
|
||||
}
|
||||
return toAddress;
|
||||
}
|
||||
|
||||
public String setContent(JSONObject jsonObject, String templatePath) {
|
||||
Context context = new Context();
|
||||
if (Objects.nonNull(jsonObject)) {
|
||||
for (String key : jsonObject.keySet()) {
|
||||
context.setVariable(key, jsonObject.get(key));
|
||||
}
|
||||
}
|
||||
return templateEngine.process(templatePath, context);
|
||||
}
|
||||
|
||||
}
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.model.dto.BasicEmailParamDTO;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MailUtil {
|
||||
|
||||
@Resource
|
||||
private JavaMailSender javaMailSender;
|
||||
|
||||
@Resource
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
/**
|
||||
* 发送邮件 - 默认发件人
|
||||
*
|
||||
* @param basicEmailParamDTO 发送邮件所需参数
|
||||
* @param fileName 附件名(如果有)
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
*/
|
||||
public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource);
|
||||
// 提取配置
|
||||
String host;
|
||||
String username;
|
||||
String password;
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
host = ((JavaMailSenderImpl) javaMailSender).getHost();
|
||||
} else {
|
||||
host = basicEmailParamDTO.getServiceAddress();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) {
|
||||
username = ((JavaMailSenderImpl) javaMailSender).getUsername();
|
||||
} else {
|
||||
username = basicEmailParamDTO.getSenderUser();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
password = ((JavaMailSenderImpl) javaMailSender).getPassword();
|
||||
} else {
|
||||
password = basicEmailParamDTO.getPassword();
|
||||
}
|
||||
return sendMail(mimeMessage, host, username, password);
|
||||
}
|
||||
|
||||
private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException {
|
||||
try {
|
||||
// 配置连接属性
|
||||
java.util.Properties props = mimeMessage.getSession().getProperties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.host", host);
|
||||
props.put("mail.user", username);
|
||||
props.put("mail.password", password);
|
||||
|
||||
// 使用 JavaMailSender 发送邮件(Spring Boot 3.x 标准方式)
|
||||
javaMailSender.send(mimeMessage);
|
||||
log.info("邮件发送成功至: {}", host);
|
||||
return 1;
|
||||
} catch (MailException e) {
|
||||
log.info("邮件发送失败:{}", e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一封邮件
|
||||
*
|
||||
* @param basicEmailParamDTO 创建邮件需要的参数
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
* @return 一封邮件
|
||||
*/
|
||||
private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
// 创建邮件对象
|
||||
MimeMessage message = javaMailSender.createMimeMessage();
|
||||
// 使用 MimeMessageHelper 简化邮件内容和附件的设置
|
||||
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
|
||||
// 设置发件人
|
||||
mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail()));
|
||||
// 设置收件人
|
||||
mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo());
|
||||
// 设置抄送人
|
||||
if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) {
|
||||
mimeMessageHelper.setCc(basicEmailParamDTO.getCc());
|
||||
}
|
||||
// 设置暗送人
|
||||
if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) {
|
||||
mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc());
|
||||
}
|
||||
// 设置邮件主题
|
||||
mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject());
|
||||
// 设置邮件内容(HTML 格式)
|
||||
mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true);
|
||||
// 设置附件
|
||||
if (inputStreamSource != null) {
|
||||
mimeMessageHelper.addAttachment(fileName, inputStreamSource);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置实体参数
|
||||
*
|
||||
* @param mailTo 接收邮件的邮箱地址
|
||||
* @param jsonObject 模板中变量的值
|
||||
* @return 返回一个MailEntity
|
||||
* @throws AddressException 邮箱地址值异常
|
||||
*/
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
// basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setSenderUserMail(CommonConstant.senderEmail);
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
// todo 邮件模板不存在的报错与重试机制
|
||||
basicEmailParamDTO.setContent(setContent(jsonObject, templatePath));
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地址转换为InternetAddress类型
|
||||
*
|
||||
* @param addressList 普通的地址字符串列表
|
||||
* @return InternetAddress类型的地址列表
|
||||
* @throws AddressException 地址异常
|
||||
*/
|
||||
public InternetAddress[] getInternetAddressList(List<String> addressList) throws AddressException {
|
||||
InternetAddress[] toAddress = new InternetAddress[addressList.size()];
|
||||
for (String address : addressList) {
|
||||
toAddress[addressList.indexOf(address)] = new InternetAddress(address);
|
||||
}
|
||||
return toAddress;
|
||||
}
|
||||
|
||||
public String setContent(JSONObject jsonObject, String templatePath) {
|
||||
Context context = new Context();
|
||||
if (Objects.nonNull(jsonObject)) {
|
||||
for (String key : jsonObject.keySet()) {
|
||||
context.setVariable(key, jsonObject.get(key));
|
||||
}
|
||||
}
|
||||
return templateEngine.process(templatePath, context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -41,6 +42,9 @@ public class MinioUtil {
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
@Value("${minio.endpoint}")
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 获取MinIO客户端实例
|
||||
*/
|
||||
@@ -958,6 +962,166 @@ public class MinioUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字符串是否为预签名URL
|
||||
* 通过检查URL中是否包含minio endpoint来判断
|
||||
*
|
||||
* @param str 待检测的字符串
|
||||
* @return true表示是预签名URL,false表示不是
|
||||
*/
|
||||
public boolean isPresignedUrl(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// 检查字符串是否是一个有效的URL
|
||||
URL url = new URL(str);
|
||||
String host = url.getHost();
|
||||
// 获取endpoint中的主机部分(去掉http://或https://)
|
||||
String endpointHost = endpoint;
|
||||
if (endpointHost.startsWith("http://")) {
|
||||
endpointHost = endpointHost.substring(7);
|
||||
} else if (endpointHost.startsWith("https://")) {
|
||||
endpointHost = endpointHost.substring(8);
|
||||
}
|
||||
// 去掉端口号
|
||||
if (endpointHost.contains(":")) {
|
||||
endpointHost = endpointHost.substring(0, endpointHost.indexOf(":"));
|
||||
}
|
||||
// 检查URL的host是否与endpoint的host匹配
|
||||
return host.equals(endpointHost);
|
||||
} catch (Exception e) {
|
||||
// 不是有效的URL
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字符串是否为MinIO逻辑路径(bucketName/objectName格式)
|
||||
* 逻辑路径特点:
|
||||
* 1. 包含 "/"(桶名和对象名之间的分隔符)
|
||||
* 2. 不是完整的URL(不以http://或https://开头)
|
||||
* 3. 路径中没有查询参数
|
||||
*
|
||||
* @param str 待检测的字符串
|
||||
* @return true表示是MinIO逻辑路径,false表示不是
|
||||
*/
|
||||
public boolean isMinioLogicalPath(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 必须是字符串
|
||||
if (!(str instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
String trimStr = str.trim();
|
||||
// 不应该以http://或https://开头
|
||||
if (trimStr.startsWith("http://") || trimStr.startsWith("https://")) {
|
||||
return false;
|
||||
}
|
||||
// 应该包含 "/"(bucket/object格式)
|
||||
if (!trimStr.contains("/")) {
|
||||
return false;
|
||||
}
|
||||
// 不应该包含空格或特殊字符
|
||||
if (trimStr.contains(" ") || trimStr.contains("\n") || trimStr.contains("\t")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将预签名URL转换为逻辑路径
|
||||
*
|
||||
* @param presignedUrl 预签名URL
|
||||
* @return 逻辑路径(格式:bucketName/objectName)
|
||||
*/
|
||||
public String getLogicalPathFromPresignedUrl(String presignedUrl) {
|
||||
try {
|
||||
// 解析URL
|
||||
URL url = new URL(presignedUrl);
|
||||
|
||||
// 获取路径部分(去掉开头的/)
|
||||
String path = url.getPath();
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
// 路径格式为 bucketName/objectName
|
||||
// Minio路径中可能包含多个/,需要正确分割
|
||||
int firstSlashIndex = path.indexOf("/");
|
||||
if (firstSlashIndex <= 0) {
|
||||
throw new MinioException("预签名URL路径格式无效,应包含桶名和对象名: " + presignedUrl);
|
||||
}
|
||||
|
||||
String bucketName = path.substring(0, firstSlashIndex);
|
||||
String objectName = path.substring(firstSlashIndex + 1);
|
||||
|
||||
// log.info("预签名URL转换成功,桶名: {}, 对象名: {}", bucketName, objectName);
|
||||
return bucketName + "/" + objectName;
|
||||
} catch (Exception e) {
|
||||
log.error("预签名URL解析失败: {}", e.getMessage(), e);
|
||||
throw new BusinessException("system.error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理MinIO资源(预签名URL或逻辑路径),统一生成预签名URL
|
||||
*
|
||||
* @param resource 预签名URL或逻辑路径
|
||||
* @param expires 过期时间(秒)
|
||||
* @return 新的预签名URL
|
||||
*/
|
||||
public String processMinioResource(String resource, int expires) {
|
||||
try {
|
||||
String logicalPath;
|
||||
if (isPresignedUrl(resource)) {
|
||||
// 是预签名URL,解析为逻辑路径
|
||||
logicalPath = getLogicalPathFromPresignedUrl(resource);
|
||||
} else if (isMinioLogicalPath(resource)) {
|
||||
// 本身就是逻辑路径
|
||||
logicalPath = resource.trim();
|
||||
} else {
|
||||
// 不认识的内容,直接返回原始值
|
||||
log.warn("未识别的MinIO资源格式: {}", resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
// 统一生成预签名URL
|
||||
return getPreSignedUrl(logicalPath, expires);
|
||||
} catch (Exception e) {
|
||||
log.error("处理MinIO资源失败: {}, error: {}", resource, e.getMessage(), e);
|
||||
// 如果失败,返回原始内容
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将任意MinIO URL转换为逻辑路径
|
||||
* 检测URL类型并转换为逻辑路径返回
|
||||
*
|
||||
* @param url 预签名URL或逻辑路径
|
||||
* @return 逻辑路径(格式:bucketName/objectName)
|
||||
* @throws MinioException 如果不是有效的MinIO资源
|
||||
*/
|
||||
public String convertToLogicalPath(String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
throw new BusinessException("url.cannot.be.empty");
|
||||
}
|
||||
if (isMinioLogicalPath(url)) {
|
||||
// 本身就是逻辑路径,直接返回
|
||||
return url.trim();
|
||||
} else if (isPresignedUrl(url)) {
|
||||
// 是预签名URL,转换为逻辑路径
|
||||
return getLogicalPathFromPresignedUrl(url);
|
||||
} else {
|
||||
// 不认识的内容,抛出异常
|
||||
throw new BusinessException("无法识别的MinIO资源格式: " + url + ",请提供有效的预签名URL或逻辑路径");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,32 +0,0 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
public class SecurityContextUtils {
|
||||
|
||||
public static AuthPrincipalVo getCurrentUser() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && authentication.getPrincipal() != null) {
|
||||
return (AuthPrincipalVo) authentication.getPrincipal();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Long getCurrentUserId() {
|
||||
AuthPrincipalVo currentUser = getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
return currentUser.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getCurrentUsername() {
|
||||
AuthPrincipalVo currentUser = getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
return currentUser.getUsername();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
131
src/main/java/com/ai/da/common/utils/TokenGenerateUtils.java
Normal file
131
src/main/java/com/ai/da/common/utils/TokenGenerateUtils.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Token 生成工具类(仅负责生成,不负责鉴权)。
|
||||
* 鉴权逻辑已迁移至 Gateway(GlobalAuthWebFilter)。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenGenerateUtils {
|
||||
|
||||
private static final String ISSUER = "DWJ";
|
||||
|
||||
@Value("${spring.security.jwtSecret:JWTSECRET}")
|
||||
private String jwtSecret;
|
||||
|
||||
@Value("${spring.security.jwtExpiration:8640000000}")
|
||||
private long jwtExpiration;
|
||||
|
||||
@Value("${spring.security.jwtTokenPrefix:Bearer-}")
|
||||
private String jwtTokenPrefix;
|
||||
|
||||
/**
|
||||
* 生成 JWT Token。
|
||||
* @param principal 用户信息
|
||||
* @return 完整的 token(含 prefix)
|
||||
*/
|
||||
public String createToken(AuthPrincipalVo principal) {
|
||||
SecretKey key = buildSigningKey();
|
||||
String token = Jwts.builder()
|
||||
.id(String.valueOf(principal.getId()))
|
||||
.subject(JSONObject.toJSONString(principal))
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.expiration(new Date(System.currentTimeMillis() + jwtExpiration))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
return jwtTokenPrefix + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Token 过期时间(毫秒)。
|
||||
*/
|
||||
public long getJwtExpiration() {
|
||||
return jwtExpiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用于邮箱变更的简化 Token。
|
||||
* @param userId 用户 ID
|
||||
* @param mailbox 新邮箱
|
||||
* @return token(不含 prefix)
|
||||
*/
|
||||
public String createMailboxToken(Long userId, String mailbox) {
|
||||
SecretKey key = buildSigningKey();
|
||||
return Jwts.builder()
|
||||
.id(String.valueOf(userId))
|
||||
.subject(mailbox + "_" + userId)
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.expiration(new Date(System.currentTimeMillis() + CommonConstant.CHANGE_MAILBOX_LINK_VALIDITY))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 Token 是否有效(签名和有效期)。
|
||||
*/
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
Claims claims = parseTokenBody(token);
|
||||
return claims != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Token 中解析用户信息。
|
||||
*/
|
||||
public AuthPrincipalVo parserToUser(String token) {
|
||||
try {
|
||||
String subject = parseTokenBody(token).getSubject();
|
||||
if (StrUtil.isNotEmpty(subject)) {
|
||||
return JSONObject.parseObject(subject, AuthPrincipalVo.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("JWT解析用户信息失败: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析邮箱变更 Token,返回 "email_id" 格式字符串。
|
||||
*/
|
||||
public String parseMailboxToken(String token) {
|
||||
return parseTokenBody(token).getSubject();
|
||||
}
|
||||
|
||||
private Claims parseTokenBody(String token) {
|
||||
SecretKey key = buildSigningKey();
|
||||
return Jwts.parser()
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
private SecretKey buildSigningKey() {
|
||||
byte[] raw = jwtSecret.getBytes(StandardCharsets.UTF_8);
|
||||
if (raw.length < 32) {
|
||||
raw = DigestUtil.sha256(raw);
|
||||
}
|
||||
return Keys.hmacShaKeyFor(raw);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.model.vo.ContestantCountVO;
|
||||
import com.ai.da.service.GlobalAwardService;
|
||||
import com.ai.da.service.upload.UploadService;
|
||||
import com.ai.da.service.upload.UploadTask;
|
||||
@@ -11,6 +12,7 @@ import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -163,6 +165,39 @@ public class GlobalAwardController {
|
||||
return Response.success(globalAwardService.checkCode(email, code));
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/export")
|
||||
@ApiOperation(value = "导出参赛者列表为Excel", notes = "导出所有参赛者信息为xlsx并触发下载")
|
||||
public void exportContestants(HttpServletResponse response) throws Exception {
|
||||
byte[] data = globalAwardService.exportContestants();
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.xlsx\"");
|
||||
response.setContentLength(data.length);
|
||||
response.getOutputStream().write(data);
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@PostMapping("/contestants/export/files")
|
||||
@ApiOperation(value = "导出参赛者文件为ZIP", notes = "根据参赛者编号范围导出PDF、视频和信息文件为ZIP,直接响应给浏览器")
|
||||
public void exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request, HttpServletResponse response) throws Exception {
|
||||
byte[] zipData = globalAwardService.exportContestantFilesAsZip(request.getMinContestantNumber(), request.getMaxContestantNumber());
|
||||
if (zipData.length == 0) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.getWriter().write("No contestants found in the specified range.");
|
||||
return;
|
||||
}
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.zip\"");
|
||||
response.setContentLength(zipData.length);
|
||||
response.getOutputStream().write(zipData);
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/count")
|
||||
@ApiOperation(value = "查询参赛者总数", notes = "查询数据库中参赛者的总数量和最大参赛者编号")
|
||||
public Response<ContestantCountVO> getContestantCount() {
|
||||
return Response.success(globalAwardService.getContestantCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ai.da.feign.gateway;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* 调用 Gateway 黑名单接口,将指定用户的 token 加入黑名单。
|
||||
* 替代原来的 SellerFeignClient.clearTokenCache。
|
||||
*/
|
||||
@FeignClient(name = "aida-gateway", path = "/internal")
|
||||
public interface GatewayFeignClient {
|
||||
|
||||
/**
|
||||
* 将用户 token 加入黑名单。
|
||||
* 后续 Gateway 会拒绝携带该用户 token 的请求。
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
Response<Void> logout(@RequestParam("userId") Long userId);
|
||||
}
|
||||
20
src/main/java/com/ai/da/feign/seller/SellerFeignClient.java
Normal file
20
src/main/java/com/ai/da/feign/seller/SellerFeignClient.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.ai.da.feign.seller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* 调用 aida-seller 设计师相关接口的 Feign 客户端
|
||||
*/
|
||||
@FeignClient(name = "aida-seller", path = "/api/designer")
|
||||
public interface SellerFeignClient {
|
||||
|
||||
@GetMapping("/check")
|
||||
Response<Boolean> checkDesignerQualification(@RequestParam("userId") Long userId);
|
||||
|
||||
@PostMapping("/cache/clear")
|
||||
Response<Void> clearTokenCache(@RequestParam("userId") Long userId);
|
||||
}
|
||||
@@ -26,6 +26,9 @@ public class Contestant {
|
||||
|
||||
private String email;
|
||||
|
||||
@TableField("contestant_number")
|
||||
private Integer contestantNumber;
|
||||
|
||||
@TableField("first_name")
|
||||
private String firstName;
|
||||
|
||||
@@ -56,6 +59,18 @@ public class Contestant {
|
||||
@TableField("video_path")
|
||||
private String videoPath;
|
||||
|
||||
@TableField("video_duration")
|
||||
private Integer videoDuration;
|
||||
|
||||
@TableField("video_size")
|
||||
private Long videoSize;
|
||||
|
||||
@TableField("pdf_size")
|
||||
private Long pdfSize;
|
||||
|
||||
@TableField("portfolio_url")
|
||||
private String portfolioUrl;
|
||||
|
||||
@TableField("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
|
||||
@@ -49,6 +49,15 @@ public class ContestantDTO {
|
||||
|
||||
@ApiModelProperty(value = "视频文件路径", required = false, example = "contestants/user@example.com/2024/01/video_1234567890.mp4")
|
||||
private String videoPath;
|
||||
|
||||
@ApiModelProperty(value = "视频时长(秒)", required = false, example = "120")
|
||||
private Integer videoDuration;
|
||||
|
||||
@ApiModelProperty(value = "视频大小(字节)", required = false, example = "10485760")
|
||||
private Long videoSize;
|
||||
|
||||
@ApiModelProperty(value = "PDF 文件大小(字节)", required = false, example = "524288")
|
||||
private Long pdfSize;
|
||||
|
||||
// /**
|
||||
// * 是否确认覆盖已存在记录(false 表示发现已有记录时仅返回 existingRecord,不覆盖)
|
||||
@@ -58,6 +67,8 @@ public class ContestantDTO {
|
||||
|
||||
@NotBlank
|
||||
private String secureToken;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 参赛者文件导出请求DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "参赛者文件导出请求", description = "用于导出指定范围的参赛者文件")
|
||||
public class ContestantExportRequest {
|
||||
|
||||
@ApiModelProperty(value = "最小参赛者编号", required = true, example = "10000")
|
||||
private Integer minContestantNumber;
|
||||
|
||||
@ApiModelProperty(value = "最大参赛者编号", required = true, example = "10010")
|
||||
private Integer maxContestantNumber;
|
||||
}
|
||||
55
src/main/java/com/ai/da/model/dto/ImageProcessRequest.java
Normal file
55
src/main/java/com/ai/da/model/dto/ImageProcessRequest.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 图片处理请求体
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ImageProcessRequest {
|
||||
|
||||
/**
|
||||
* OSS桶名(bucket_name)
|
||||
*/
|
||||
private String bucket_name;
|
||||
|
||||
/**
|
||||
* OSS对象名(object_name)
|
||||
*/
|
||||
private String object_name;
|
||||
|
||||
/**
|
||||
* 输入图片路径列表(input_image_paths)
|
||||
*/
|
||||
private List<String> input_image_paths;
|
||||
|
||||
/**
|
||||
* 图像宽度(width)
|
||||
*/
|
||||
private Integer width;
|
||||
|
||||
/**
|
||||
* 图像高度(height)
|
||||
*/
|
||||
private Integer height;
|
||||
|
||||
/**
|
||||
* 文本提示(prompt)
|
||||
*/
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 推理步数(steps)
|
||||
*/
|
||||
private Integer steps;
|
||||
|
||||
/**
|
||||
* 引导系数(guidance)
|
||||
*/
|
||||
private Double guidance;
|
||||
|
||||
}
|
||||
17
src/main/java/com/ai/da/model/vo/ContestantCountVO.java
Normal file
17
src/main/java/com/ai/da/model/vo/ContestantCountVO.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.ai.da.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ContestantCountVO {
|
||||
|
||||
private Long count;
|
||||
|
||||
private Integer maxContestantNumber;
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.ai.da.python;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import com.ai.da.common.RabbitMQ.RabbitMQProperties;
|
||||
import com.ai.da.common.config.FileProperties;
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.enums.*;
|
||||
import com.ai.da.common.utils.*;
|
||||
@@ -20,6 +22,7 @@ import com.ai.da.model.vo.*;
|
||||
import com.ai.da.python.vo.*;
|
||||
import com.ai.da.service.DesignHistoryService;
|
||||
import com.ai.da.service.PythonTAllInfoService;
|
||||
import com.ai.da.service.RabbitMQService;
|
||||
import com.ai.da.service.SysFileService;
|
||||
import com.alibaba.fastjson.*;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
@@ -39,6 +42,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
@@ -68,10 +72,12 @@ public class PythonService {
|
||||
private String accessPythonPort;
|
||||
@Value("${minio.bucketName.gradient}")
|
||||
private String gradientBucketName;
|
||||
@Value("${minio.bucketName.users}")
|
||||
private String userBucketName;
|
||||
@Value("${access.python.generate_sr_port}")
|
||||
private String srServicePort;
|
||||
|
||||
@Value("${design.callback.url}")
|
||||
@Value("${design.callback.url.aida}")
|
||||
private String callbackUrl;
|
||||
|
||||
@Resource
|
||||
@@ -83,6 +89,12 @@ public class PythonService {
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Resource
|
||||
private RabbitMQService rabbitMQService;
|
||||
|
||||
@Resource
|
||||
private RabbitMQProperties rabbitMQProperties;
|
||||
|
||||
/**
|
||||
* 生成打印的图片 二合一 (废弃于2024/01/02)
|
||||
*
|
||||
@@ -279,7 +291,7 @@ public class PythonService {
|
||||
|
||||
if (isDuplicate) {
|
||||
elementVO.setHasUseMd5List(beforeAssemblyHasUseMd5List);
|
||||
i --;
|
||||
i--;
|
||||
|
||||
switch (designPrintPictureType) {
|
||||
case PIN:
|
||||
@@ -406,13 +418,14 @@ public class PythonService {
|
||||
// if (CollectionUtil.isEmpty(pinData)) {
|
||||
// return 0;
|
||||
// }
|
||||
//// long topNum = sketchBoardElements.stream()
|
||||
//// .filter(skecth -> skecth.getHasPin() == 1
|
||||
//// && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
|
||||
//// long bottomNum = sketchBoardElements.stream()
|
||||
//// .filter(skecth -> skecth.getHasPin() == 1
|
||||
//// && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
|
||||
//// int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue();
|
||||
|
||||
/// / long topNum = sketchBoardElements.stream()
|
||||
/// / .filter(skecth -> skecth.getHasPin() == 1
|
||||
/// / && DesignPythonItem.OUTWEAR_DRESS_BLOUSE.contains(skecth.getLevel2Type())).count();
|
||||
/// / long bottomNum = sketchBoardElements.stream()
|
||||
/// / .filter(skecth -> skecth.getHasPin() == 1
|
||||
/// / && DesignPythonItem.SKIRT_TROUSERS.contains(skecth.getLevel2Type())).count();
|
||||
/// / int num = Arrays.asList(topNum, bottomNum).stream().max(Comparator.comparing(Long::valueOf)).get().intValue();
|
||||
// int num = pinData.size();
|
||||
// return Math.min(num, 8);
|
||||
// }
|
||||
@@ -560,12 +573,12 @@ public class PythonService {
|
||||
return 0;
|
||||
} else {
|
||||
long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count();
|
||||
if (designNum - pinNum < 0){
|
||||
return RandomsUtil.randomSysFile(0L, (long) (pinNum/2 + 1));
|
||||
} else if (designNum - pinNum < designNum/2) {
|
||||
if (designNum - pinNum < 0) {
|
||||
return RandomsUtil.randomSysFile(0L, (long) (pinNum / 2 + 1));
|
||||
} else if (designNum - pinNum < designNum / 2) {
|
||||
return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1);
|
||||
} else {
|
||||
return RandomsUtil.randomSysFile(0L, (long) (designNum/2 + 1));
|
||||
return RandomsUtil.randomSysFile(0L, (long) (designNum / 2 + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,8 +693,7 @@ public class PythonService {
|
||||
|
||||
// 其他所有情况,都回退到使用系统推荐
|
||||
String categoryParam = elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase();
|
||||
List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(),elementVO.getStyle());
|
||||
|
||||
List<String> recommentdUrlList = getSystemSketchByCategory(categoryParam, elementVO.getBrandId(), elementVO.getBrandScale(), elementVO.getStyle());
|
||||
if (CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||
}
|
||||
@@ -1039,7 +1051,7 @@ public class PythonService {
|
||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||
}else {
|
||||
} else {
|
||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||
}
|
||||
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
||||
@@ -1062,7 +1074,7 @@ public class PythonService {
|
||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||
}else {
|
||||
} else {
|
||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||
}
|
||||
|
||||
@@ -1098,7 +1110,7 @@ public class PythonService {
|
||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||
}else {
|
||||
} else {
|
||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||
}
|
||||
// JSONObject attributeRecognition = getAttributeRecognitionBySameCategory(element, validateElementVO.getModelSex());
|
||||
@@ -1122,7 +1134,7 @@ public class PythonService {
|
||||
if (!CollectionUtils.isEmpty(recommentdUrlList)) {
|
||||
String recommendSystemSketch = recommentdUrlList.get(0);
|
||||
return coverSystemSketchUrlToDesignPythonItem(recommendSystemSketch, category, validateElementVO);
|
||||
}else {
|
||||
} else {
|
||||
throw new BusinessException("failed.to.obtain.system.sketch.recommendation");
|
||||
}
|
||||
// String tableName = getTableName(validateElementVO.getModelSex(), category);
|
||||
@@ -2190,7 +2202,7 @@ public class PythonService {
|
||||
basic.setScale_earrings(0.16);
|
||||
if (Objects.nonNull(designLibraryModelPointVO)) {
|
||||
basic.setBody_point_test(getMap(designLibraryModelPointVO));
|
||||
}else {
|
||||
} else {
|
||||
basic.setBody_point_test(getMap(designLibraryModelPoint));
|
||||
}
|
||||
return basic;
|
||||
@@ -2851,13 +2863,17 @@ public class PythonService {
|
||||
gradientString = JSONObject.toJSONString(designSingleItem.getGradient());
|
||||
}
|
||||
|
||||
PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
||||
PrintToPython printToPython;
|
||||
printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
||||
designSingleItem.getPartialDesign().getPartialDesignMinioPath());
|
||||
resolveDesignElement(designSingleItem.getTrims(), printToPython);
|
||||
log.info("组装参数【服装:{}的maskUrl: {}】",designSingleItem.getType(), designSingleItem.getMaskUrl());
|
||||
/*PrintToPython printToPython = resolveDesignSinglePrint(designSingleItem.getPrintObject().getPrints(),
|
||||
designSingleItem.getPartialDesign().getPartialDesignMinioPath());*/
|
||||
// resolveDesignElement(designSingleItem.getTrims(), printToPython);
|
||||
log.info("组装参数【服装:{}的maskUrl: {}】", designSingleItem.getType(), designSingleItem.getMaskUrl());
|
||||
|
||||
String mergeImagePath = !StringUtil.isNullOrEmpty(printToPython.getPartial())
|
||||
? printToPython.getPartial() : designSingleItem.getPath();
|
||||
String partialDesign = designSingleItem.getPartialDesign().getPartialDesignMinioPath();
|
||||
String mergeImagePath = !StringUtil.isNullOrEmpty(partialDesign)
|
||||
? partialDesign : designSingleItem.getPath();
|
||||
response.add(new DesignPythonItem(
|
||||
designSingleItem.getType(),
|
||||
designSingleItem.getPath(),
|
||||
@@ -2880,7 +2896,7 @@ public class PythonService {
|
||||
|
||||
});
|
||||
|
||||
if (singleOverall.equals("overall")){
|
||||
if (singleOverall.equals("overall")) {
|
||||
String bodyPath;
|
||||
if (Objects.nonNull(designLibraryModelPoint)) {
|
||||
bodyPath = designLibraryModelPoint.getTemplateUrl();
|
||||
@@ -2896,12 +2912,12 @@ public class PythonService {
|
||||
|
||||
private PrintToPython resolveDesignSinglePrint(List<DesignSinglePrint> printObject, String partialDesign) {
|
||||
PrintToPython printToPython = new PrintToPython();
|
||||
DesignPythonItemPrint printSingle = new DesignPythonItemPrint();
|
||||
// DesignPythonItemPrint printSingle = new DesignPythonItemPrint();
|
||||
DesignPythonItemPrint printOverall = new DesignPythonItemPrint();
|
||||
printToPython.setSingle(printSingle);
|
||||
// printToPython.setSingle(printSingle);
|
||||
printToPython.setOverall(printOverall);
|
||||
printToPython.setPartial(StringUtil.isNullOrEmpty(partialDesign) ? null : partialDesign);
|
||||
if (Objects.isNull(printObject) || printObject.isEmpty()){
|
||||
if (Objects.isNull(printObject) || printObject.isEmpty()) {
|
||||
return printToPython;
|
||||
}
|
||||
|
||||
@@ -2932,12 +2948,12 @@ public class PythonService {
|
||||
Integer priority = p.getPriority();
|
||||
setUriToMinioPath(p);
|
||||
// todo 下标越界问题
|
||||
if (p.getIfSingle()){
|
||||
if (p.getIfSingle()) {
|
||||
locationS.set(priority - 1, p.getLocation());
|
||||
scaleS.set(priority - 1, p.getScale());
|
||||
angleS.set( priority - 1, p.getAngle());
|
||||
angleS.set(priority - 1, p.getAngle());
|
||||
pathsS.set(priority - 1, p.getMinIOPath());
|
||||
}else {
|
||||
} else {
|
||||
locationO.set(priority - 1, p.getLocation());
|
||||
scaleO.set(priority - 1, p.getScale());
|
||||
angleO.set(priority - 1, p.getAngle());
|
||||
@@ -2945,14 +2961,14 @@ public class PythonService {
|
||||
}
|
||||
// log.info("本次print打点locations###{}###fileVO{}", p.getLocation(), JSON.toJSONString(fileVO));
|
||||
});
|
||||
locationS.removeAll(Collections.singleton(null));
|
||||
/*locationS.removeAll(Collections.singleton(null));
|
||||
scaleS.removeAll(Collections.singleton(null));
|
||||
angleS.removeAll(Collections.singleton(null));
|
||||
pathsS.removeAll(Collections.singleton(null));
|
||||
printSingle.setLocation(locationS);
|
||||
printSingle.setPrint_scale_list(scaleS);
|
||||
printSingle.setPrint_angle_list(angleS);
|
||||
printSingle.setPrint_path_list(pathsS);
|
||||
printSingle.setPrint_path_list(pathsS);*/
|
||||
|
||||
locationO.removeAll(Collections.singleton(null));
|
||||
scaleO.removeAll(Collections.singleton(null));
|
||||
@@ -2967,9 +2983,9 @@ public class PythonService {
|
||||
}
|
||||
|
||||
// 对印花类型为Generate的图片路径进行特殊处理
|
||||
private void setUriToMinioPath(DesignSinglePrint print){
|
||||
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")){
|
||||
if (!StringUtil.isNullOrEmpty(print.getPath())){
|
||||
private void setUriToMinioPath(DesignSinglePrint print) {
|
||||
if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")) {
|
||||
if (!StringUtil.isNullOrEmpty(print.getPath())) {
|
||||
try {
|
||||
URI uri = new URI(print.getPath());
|
||||
String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png
|
||||
@@ -3329,7 +3345,7 @@ public class PythonService {
|
||||
throw new BusinessException("system error!");
|
||||
}
|
||||
|
||||
public Boolean generateSketchOrPrint(String params, String port, String servicePath) {
|
||||
public Boolean generateSketchOrPrint(String params, String port, String servicePath, String taskId) {
|
||||
//限流校验
|
||||
// AccessLimitUtils.validate("generateSketchOrPrint", 5);
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
@@ -3391,12 +3407,36 @@ public class PythonService {
|
||||
if (result && jsonObject.get("code").equals(200)) {
|
||||
log.info("Generate##responseObject###{}", jsonObject);
|
||||
// return setGenerateImageList(jsonObject.getJSONObject("data"));
|
||||
if (servicePath == CommonConstant.GENERATE_PATH_FLUX2_KLEIN) {
|
||||
//放入结果到mq
|
||||
JSONObject data = jsonObject.getJSONObject("data");
|
||||
String outputPath = data.getString("output_path");
|
||||
|
||||
|
||||
Map<String, String> mqMessage = new HashMap<>();
|
||||
mqMessage.put("tasks_id", taskId);
|
||||
mqMessage.put("status", "SUCCESS");
|
||||
mqMessage.put("message", "success");
|
||||
mqMessage.put("image_url", outputPath);
|
||||
mqMessage.put("category", "");
|
||||
String mqMessageBody = JSON.toJSONString(mqMessage);
|
||||
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
} else {
|
||||
log.info("generateSketchOrPrintPrint失败###{}", jsonObject);
|
||||
log.info("Generate Exception! Code : " + jsonObject.get("code"));
|
||||
Map<String, String> mqMessage = new HashMap<>();
|
||||
mqMessage.put("tasks_id", taskId);
|
||||
mqMessage.put("status", "ERROR");
|
||||
mqMessage.put("message", "");
|
||||
mqMessage.put("image_url", "");
|
||||
mqMessage.put("category", "");
|
||||
String mqMessageBody = JSON.toJSONString(mqMessage);
|
||||
rabbitMQService.publishMessageToGenerateResult(mqMessageBody);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Response sendPostToModel(String content, String portAndRoute, String functionName) {
|
||||
@@ -3438,7 +3478,10 @@ public class PythonService {
|
||||
|
||||
return imageUrlList;
|
||||
}*/
|
||||
/** 废弃状态 */
|
||||
|
||||
/**
|
||||
* 废弃状态
|
||||
*/
|
||||
public String composeLayers(List<OutfitDetailPythonItem> layersDetail) {
|
||||
HashMap<String, List<OutfitDetailPythonItem>> layers = new HashMap<>();
|
||||
HashMap<String, HashMap<String, List<OutfitDetailPythonItem>>> content = new HashMap<>();
|
||||
@@ -3749,7 +3792,7 @@ public class PythonService {
|
||||
throw new BusinessException("relightImage.interface.exception");
|
||||
}
|
||||
|
||||
public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl){
|
||||
public String imageToSketch(String imagePath, String bucket, String objectName, String styleCode, String styleImageUrl) {
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
|
||||
@@ -3796,7 +3839,7 @@ public class PythonService {
|
||||
String sketchResult = jsonObject.get("data").toString();
|
||||
log.info("ImageToSketch 结果 : {}", sketchResult);
|
||||
return sketchResult;
|
||||
}else {
|
||||
} else {
|
||||
log.info("ImageToSketch 失败。 Response code {}", responseCode);
|
||||
throw new BusinessException("ImageToSketch 失败。 Response code " + responseCode);
|
||||
}
|
||||
@@ -3849,7 +3892,7 @@ public class PythonService {
|
||||
throw new BusinessException("bright.interface.exception");
|
||||
}
|
||||
|
||||
public JSONObject attributeRecognition(List<String> pictureUrls,List<String> ids, List<String> category) {
|
||||
public JSONObject attributeRecognition(List<String> pictureUrls, List<String> ids, List<String> category) {
|
||||
//限流校验
|
||||
AccessLimitUtils.validate("attributeRecognition", 20);
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
@@ -3881,7 +3924,7 @@ public class PythonService {
|
||||
} catch (IOException ioException) {
|
||||
log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException));
|
||||
}
|
||||
log.info("识别python对应的属性标签值结果###{}",bodyStr.trim());
|
||||
log.info("识别python对应的属性标签值结果###{}", bodyStr.trim());
|
||||
//去除限流
|
||||
AccessLimitUtils.validateOut("attributeRecognition");
|
||||
if (Objects.isNull(response)) {
|
||||
@@ -3965,7 +4008,7 @@ public class PythonService {
|
||||
throw new BusinessException("design.interface.exception");
|
||||
}
|
||||
|
||||
public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale,String style) {
|
||||
public List<String> getSystemSketchByCategory(String category, Long brandId, Double brandScale, String style) {
|
||||
//******3.1.2版本临时使用java推荐方案去解决style未使用的问题**********
|
||||
// try {
|
||||
// //使用新库attribute_retrieval_style,表命名修改为elementVO.getModelSex().toLowerCase() + "_" + styleCategory.toLowerCase()比如female_skirt,与传入的category保持一致
|
||||
@@ -3989,6 +4032,14 @@ public class PythonService {
|
||||
// throw new BusinessException("system.error");
|
||||
// }
|
||||
//**********************end***********************************
|
||||
//临时补丁
|
||||
if (category != null && style != null) {
|
||||
String categoryPrefix = category.split("_")[0];
|
||||
if ("male".equals(categoryPrefix) &&
|
||||
(style.equalsIgnoreCase("lolita") || style.equalsIgnoreCase("romantic"))) {
|
||||
style = null;
|
||||
}
|
||||
}
|
||||
AuthPrincipalVo userHolder = UserContext.getUserHolder();
|
||||
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
@@ -4110,6 +4161,7 @@ public class PythonService {
|
||||
//生成失败
|
||||
throw new BusinessException("segProduct.interface.exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发 seg_anything 请求到 python 服务
|
||||
* 请求 body 由调用方组装(包含 user_id, image_path, type, points, labels, box 等)
|
||||
@@ -4122,6 +4174,9 @@ public class PythonService {
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build();
|
||||
MediaType mediaType = MediaType.parse("application/json");
|
||||
content.put("bucket", userBucketName);
|
||||
content.put("object_name", content.get("user_id") + "/" + "segment" + "/" + UUID.randomUUID() + ".png");
|
||||
content.remove("user_id");
|
||||
RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
|
||||
|
||||
String url = accessPythonIp + ":" + accessPythonPort + "/api/seg_anything";
|
||||
@@ -4152,18 +4207,18 @@ public class PythonService {
|
||||
if (responseObject != null) {
|
||||
JSONObject dataObj = responseObject.getJSONObject("data");
|
||||
if (dataObj != null) {
|
||||
String output = dataObj.getString("output");
|
||||
if (!StringUtil.isNullOrEmpty(output)) {
|
||||
try {
|
||||
String presigned = minioUtil.getPreSignedUrl(output, 24 * 60);
|
||||
return presigned;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to generate presigned url for {}: {}", output, e.getMessage());
|
||||
throw new BusinessException("segAnything.presign.failed");
|
||||
}
|
||||
String output = dataObj.getString("output");
|
||||
if (!StringUtil.isNullOrEmpty(output)) {
|
||||
try {
|
||||
String presigned = minioUtil.getPreSignedUrl(output, 24 * 60);
|
||||
return presigned;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to generate presigned url for {}: {}", output, e.getMessage());
|
||||
throw new BusinessException("segAnything.presign.failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error("PythonService##segAnything post-process error###{}", ex.getMessage());
|
||||
@@ -4173,7 +4228,7 @@ public class PythonService {
|
||||
throw new BusinessException("segAnything.missing.output");
|
||||
}
|
||||
throw new BusinessException("segAnything.interface.exception");
|
||||
} catch (IOException |JSONException e) {
|
||||
} catch (IOException | JSONException e) {
|
||||
log.error("PythonService##segAnything异常###{}", e.getMessage());
|
||||
throw new BusinessException("segAnything.interface.exception");
|
||||
}
|
||||
@@ -4181,6 +4236,7 @@ public class PythonService {
|
||||
log.error("PythonService##segAnything异常response###{}", response);
|
||||
throw new BusinessException("segAnything.interface.exception");
|
||||
}
|
||||
|
||||
public Boolean poseTransformation(JSONObject content, String apiUri) {
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
@@ -4284,7 +4340,7 @@ public class PythonService {
|
||||
String modifiedModel = jsonObject.get("data").toString();
|
||||
log.info("modifyModelProportion 结果 : {}", modifiedModel);
|
||||
return modifiedModel;
|
||||
}else {
|
||||
} else {
|
||||
log.info("modifyModelProportion 失败。 Response code {}", responseCode);
|
||||
throw new BusinessException("modifyModelProportion 失败。 Response code " + responseCode);
|
||||
}
|
||||
@@ -4387,10 +4443,11 @@ public class PythonService {
|
||||
log.info("imageSegmentation 结果 : {}", jsonObject.get("data").toString());
|
||||
List<ImageSegmentation.ImageDate> seg = JSONObject.parseObject(
|
||||
jsonObject.get("data").toString(),
|
||||
new TypeReference<List<ImageSegmentation.ImageDate>>() {}
|
||||
new TypeReference<List<ImageSegmentation.ImageDate>>() {
|
||||
}
|
||||
);
|
||||
return seg;
|
||||
}else {
|
||||
} else {
|
||||
log.info("imageSegmentation 失败。 Response code {}", responseCode);
|
||||
throw new BusinessException("imageSegmentation 失败。 Response code " + responseCode);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.ai.da.python.vo;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DesignPythonItemPrint {
|
||||
@@ -53,7 +53,7 @@ public class DesignPythonItemPrint {
|
||||
if (ifDesign){
|
||||
this.print_path_list = print_path_list;
|
||||
this.location = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
|
||||
this.print_scale_list = Collections.singletonList(Arrays.asList(0.0f, 0.0f));
|
||||
this.print_scale_list = Collections.singletonList(Arrays.asList(1.0f, 1.0f));
|
||||
this.print_angle_list = Arrays.asList(0.0, 0.0);
|
||||
}
|
||||
|
||||
|
||||
37
src/main/java/com/ai/da/seller/DesignUrlsDTO.java
Normal file
37
src/main/java/com/ai/da/seller/DesignUrlsDTO.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.ai.da.seller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计URLs DTO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "设计URLs数据传输对象")
|
||||
public class DesignUrlsDTO {
|
||||
|
||||
/**
|
||||
* 设计项ID
|
||||
*/
|
||||
@Schema(description = "设计项ID", example = "1")
|
||||
private Long designItemId;
|
||||
|
||||
/**
|
||||
* TO_PRODUCT_IMAGE类型的URL列表
|
||||
*/
|
||||
@Schema(description = "TO_PRODUCT_IMAGE类型的URL列表")
|
||||
private List<String> toProductImageUrls;
|
||||
|
||||
/**
|
||||
* DesignItemDetail的path列表
|
||||
*/
|
||||
@Schema(description = "DesignItemDetail的path列表")
|
||||
private List<String> clothes;
|
||||
|
||||
/**
|
||||
* 姿势转换视频信息列表
|
||||
*/
|
||||
@Schema(description = "姿势转换视频信息列表")
|
||||
private List<PoseTransformationVideoDTO> videos;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ai.da.seller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 姿势转换视频信息DTO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "姿势转换视频信息数据传输对象")
|
||||
public class PoseTransformationVideoDTO {
|
||||
|
||||
/**
|
||||
* GIF第一帧截图URL
|
||||
*/
|
||||
@Schema(description = "GIF第一帧截图URL")
|
||||
private String firstFrameUrl;
|
||||
|
||||
/**
|
||||
* GIF视频URL
|
||||
*/
|
||||
@Schema(description = "GIF视频URL")
|
||||
private String gifUrl;
|
||||
|
||||
/**
|
||||
* 视频URL
|
||||
*/
|
||||
@Schema(description = "视频URL")
|
||||
private String videoUrl;
|
||||
}
|
||||
44
src/main/java/com/ai/da/seller/SellerController.java
Normal file
44
src/main/java/com/ai/da/seller/SellerController.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.ai.da.seller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.service.UserLikeGroupService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Seller Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/seller")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Seller", description = "Seller相关接口")
|
||||
public class SellerController {
|
||||
|
||||
private final UserLikeGroupService userLikeGroupService;
|
||||
|
||||
/**
|
||||
* 根据designItemId列表获取设计相关的URL列表
|
||||
* @param designItemIds designItemId列表
|
||||
* @return 设计URLs DTO列表
|
||||
*/
|
||||
@GetMapping("/sketchDetail")
|
||||
@Operation(summary = "获取设计相关URL列表", description = "根据designItemId列表获取设计相关的URL列表,包括TO_PRODUCT_IMAGE类型的URL和DesignItemDetail的path列表")
|
||||
public Response<List<DesignUrlsDTO>> getDesignUrlsByDesignItemIds(
|
||||
@Parameter(description = "设计项ID列表", required = true, example = "1,2,3")
|
||||
@RequestParam List<Long> designItemIds) {
|
||||
List<DesignUrlsDTO> designUrlsDTOList = new ArrayList<>();
|
||||
for (Long designItemId : designItemIds) {
|
||||
DesignUrlsDTO designUrlsDTO = userLikeGroupService.getToProductImageUrlsByDesignItemId(designItemId);
|
||||
designUrlsDTOList.add(designUrlsDTO);
|
||||
}
|
||||
return Response.success(designUrlsDTOList);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.ai.da.service;
|
||||
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.model.vo.ContestantCountVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -20,6 +21,31 @@ public interface GlobalAwardService {
|
||||
CheckOTPVO checkCode(String email, String otp);
|
||||
|
||||
void checkSecurityToken(String email, String securityToken);
|
||||
|
||||
/**
|
||||
* 导出参赛者列表为 Excel(二进制)
|
||||
* @return Excel 文件的字节数组
|
||||
*/
|
||||
byte[] exportContestants() throws Exception;
|
||||
|
||||
/**
|
||||
* 将参赛者列表导出并保存到服务端本地目录(使用服务配置的 uploadDir/exports)
|
||||
*/
|
||||
void saveContestantsToLocal() throws Exception;
|
||||
|
||||
/**
|
||||
* 将参赛者文件打包为 ZIP 并返回字节数组(不落盘,直接响应给浏览器)
|
||||
* @param minContestantNumber 最小参赛者编号
|
||||
* @param maxContestantNumber 最大参赛者编号
|
||||
* @return ZIP 文件的字节数组
|
||||
*/
|
||||
byte[] exportContestantFilesAsZip(Integer minContestantNumber, Integer maxContestantNumber) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询参赛者总数和最大参赛者编号
|
||||
* @return 参赛者数量和最大参赛者编号
|
||||
*/
|
||||
ContestantCountVO getContestantCount();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ public interface RabbitMQService {
|
||||
|
||||
void publishMessageToGenerate(String message);
|
||||
|
||||
void publishMessageToGenerateResult(String message);
|
||||
|
||||
void publishMessageToSR(String message);
|
||||
|
||||
Integer getMessageCount(String queueUrl);
|
||||
|
||||
@@ -1,124 +1,132 @@
|
||||
package com.ai.da.service;
|
||||
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.mapper.primary.entity.*;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import io.minio.errors.MinioException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*
|
||||
* @author yanglei
|
||||
* @since 2022-09-11
|
||||
*/
|
||||
public interface UserLikeGroupService extends IService<UserLikeGroup> {
|
||||
|
||||
void deleteUserGroup(Long userGroupId);
|
||||
|
||||
HistoryUpdateVO updateUserGroupName(Long userGroupId, String userGroupName, String timeZone);
|
||||
|
||||
Long insertUserGroup(Long userId, Long collectionId, String timeZone, Long projectId);
|
||||
|
||||
/**
|
||||
* choose
|
||||
*
|
||||
* @param userGroupId
|
||||
* @return
|
||||
*/
|
||||
UserLikeChooseVO choose(Long userGroupId);
|
||||
|
||||
ProjectChooseVO choose(ProjectDTO projectDTO);
|
||||
|
||||
UserLikeGroup getByProjectId(Long projectId);
|
||||
|
||||
void deleteTrialData(Long id);
|
||||
|
||||
void updateDate(Long id, String timeZone);
|
||||
|
||||
Long exportSave(MultipartFile file, Long projectId, String module, Long designItemDetailId);
|
||||
|
||||
List<ToProductImageResultVO> toProduct(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
void toProduct(String taskId);
|
||||
|
||||
ToProductElementVO toProductImageElementUpload(MultipartFile file, Long projectId, String type);
|
||||
|
||||
CollectionSort productImageLike(ProductImageLikeDTO productImageLikeDTO);
|
||||
|
||||
List<MagicToolResultVO> getToProductImageResultList(List<String> taskIdList);
|
||||
|
||||
JSONObject exportSearch(ExportSearchDTO exportSearchDTO);
|
||||
|
||||
CanvasElementUpload canvasElementUpload(MultipartFile file);
|
||||
|
||||
List<ToProductImageResultVO> productImageLikeList(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
Boolean productImageUnLike(ProductImageLikeDTO productImageLikeDTO);
|
||||
|
||||
void relight(String taskId);
|
||||
|
||||
List<ToProductImageResultVO> relight(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
List<MagicToolResultVO> getRelightResult(List<String> taskIdList);
|
||||
|
||||
void deleteToProductRelightResult(Long id, Long projectId, String type);
|
||||
|
||||
String likeHistoryRelSketch();
|
||||
|
||||
String download();
|
||||
|
||||
Boolean productImageInitialize(ProductImageInitializeDTO productImageInitializeDTO);
|
||||
|
||||
InitializeProgressVO getInitializeProgress(ProductImageInitializeDTO productImageInitializeDTO);
|
||||
|
||||
IPage<ProjectVO> getPage(ProjectQueryDTO projectQueryDTO);
|
||||
|
||||
ModuleChooseVO getModuleContent(ProjectDTO projectDTO);
|
||||
|
||||
ModuleChooseVO saveModuleContent(ModuleSaveDTO moduleSaveDTO);
|
||||
|
||||
QueryLibraryPageVO getMannequinDetail(MannequinDTO mannequinDTO);
|
||||
|
||||
BrandLogoUploadVO brandLogoUpload(MultipartFile file);
|
||||
|
||||
Boolean brandDNASaveOrUpdate(BrandDNADTO brandDNADTO);
|
||||
|
||||
LibraryUpdateVo brandDNAUpload(MultipartFile file, Long brandId) throws IOException;
|
||||
|
||||
PageBaseResponse<BrandDNAVO> brandDNAPage(BrandDNAQueryDTO brandDNAQueryDTO);
|
||||
|
||||
BrandDNAGenerateVO brandDNAGenerate(String prompt);
|
||||
|
||||
IPage<ThreeDLayoutVO> getThreeDLayoutPage(ThreeDLayoutQueryDTO threeDLayoutQueryDTO);
|
||||
|
||||
ThreeDVO getLayoutDetail(Long threeDSimpleId);
|
||||
|
||||
ThreeDSizeVO getThreeDSize(Long threeDSimpleId);
|
||||
|
||||
void getThreeDGlb(Long threeDSimpleId, HttpServletResponse response) throws MinioException, IOException;
|
||||
|
||||
String downloadZip(Long threeDSimpleId, String sizeType, String size, HttpServletResponse response) throws MinioException, IOException;
|
||||
|
||||
Boolean delete(Long projectId);
|
||||
|
||||
Boolean brandDNADelete(BrandDNADTO brandDNADTO);
|
||||
|
||||
void toProductBatch(String taskId, String url, String progress) throws InterruptedException;
|
||||
|
||||
void relightBatch(String taskId, String url, String progress);
|
||||
|
||||
Boolean collectionLikeUpdate(CollectionLikeUpdateDTO collectionLikeUpdateDTO);
|
||||
|
||||
Boolean toProductImageElementDelete(Long id);
|
||||
|
||||
ToProductElementVO convertRelightElement(Long id);
|
||||
}
|
||||
package com.ai.da.service;
|
||||
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.mapper.primary.entity.*;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.seller.DesignUrlsDTO;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import io.minio.errors.MinioException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*
|
||||
* @author yanglei
|
||||
* @since 2022-09-11
|
||||
*/
|
||||
public interface UserLikeGroupService extends IService<UserLikeGroup> {
|
||||
|
||||
void deleteUserGroup(Long userGroupId);
|
||||
|
||||
HistoryUpdateVO updateUserGroupName(Long userGroupId, String userGroupName, String timeZone);
|
||||
|
||||
Long insertUserGroup(Long userId, Long collectionId, String timeZone, Long projectId);
|
||||
|
||||
/**
|
||||
* choose
|
||||
*
|
||||
* @param userGroupId
|
||||
* @return
|
||||
*/
|
||||
UserLikeChooseVO choose(Long userGroupId);
|
||||
|
||||
ProjectChooseVO choose(ProjectDTO projectDTO);
|
||||
|
||||
UserLikeGroup getByProjectId(Long projectId);
|
||||
|
||||
void deleteTrialData(Long id);
|
||||
|
||||
void updateDate(Long id, String timeZone);
|
||||
|
||||
Long exportSave(MultipartFile file, Long projectId, String module, Long designItemDetailId);
|
||||
|
||||
List<ToProductImageResultVO> toProduct(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
void toProduct(String taskId);
|
||||
|
||||
ToProductElementVO toProductImageElementUpload(MultipartFile file, Long projectId, String type);
|
||||
|
||||
CollectionSort productImageLike(ProductImageLikeDTO productImageLikeDTO);
|
||||
|
||||
List<MagicToolResultVO> getToProductImageResultList(List<String> taskIdList);
|
||||
|
||||
JSONObject exportSearch(ExportSearchDTO exportSearchDTO);
|
||||
|
||||
CanvasElementUpload canvasElementUpload(MultipartFile file);
|
||||
|
||||
List<ToProductImageResultVO> productImageLikeList(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
Boolean productImageUnLike(ProductImageLikeDTO productImageLikeDTO);
|
||||
|
||||
void relight(String taskId);
|
||||
|
||||
List<ToProductImageResultVO> relight(ToProductImageDTO toProductImageDTO);
|
||||
|
||||
List<MagicToolResultVO> getRelightResult(List<String> taskIdList);
|
||||
|
||||
void deleteToProductRelightResult(Long id, Long projectId, String type);
|
||||
|
||||
String likeHistoryRelSketch();
|
||||
|
||||
String download();
|
||||
|
||||
Boolean productImageInitialize(ProductImageInitializeDTO productImageInitializeDTO);
|
||||
|
||||
InitializeProgressVO getInitializeProgress(ProductImageInitializeDTO productImageInitializeDTO);
|
||||
|
||||
IPage<ProjectVO> getPage(ProjectQueryDTO projectQueryDTO);
|
||||
|
||||
ModuleChooseVO getModuleContent(ProjectDTO projectDTO);
|
||||
|
||||
ModuleChooseVO saveModuleContent(ModuleSaveDTO moduleSaveDTO);
|
||||
|
||||
QueryLibraryPageVO getMannequinDetail(MannequinDTO mannequinDTO);
|
||||
|
||||
BrandLogoUploadVO brandLogoUpload(MultipartFile file);
|
||||
|
||||
Boolean brandDNASaveOrUpdate(BrandDNADTO brandDNADTO);
|
||||
|
||||
LibraryUpdateVo brandDNAUpload(MultipartFile file, Long brandId) throws IOException;
|
||||
|
||||
PageBaseResponse<BrandDNAVO> brandDNAPage(BrandDNAQueryDTO brandDNAQueryDTO);
|
||||
|
||||
BrandDNAGenerateVO brandDNAGenerate(String prompt);
|
||||
|
||||
IPage<ThreeDLayoutVO> getThreeDLayoutPage(ThreeDLayoutQueryDTO threeDLayoutQueryDTO);
|
||||
|
||||
ThreeDVO getLayoutDetail(Long threeDSimpleId);
|
||||
|
||||
ThreeDSizeVO getThreeDSize(Long threeDSimpleId);
|
||||
|
||||
void getThreeDGlb(Long threeDSimpleId, HttpServletResponse response) throws MinioException, IOException;
|
||||
|
||||
String downloadZip(Long threeDSimpleId, String sizeType, String size, HttpServletResponse response) throws MinioException, IOException;
|
||||
|
||||
Boolean delete(Long projectId);
|
||||
|
||||
Boolean brandDNADelete(BrandDNADTO brandDNADTO);
|
||||
|
||||
void toProductBatch(String taskId, String url, String progress) throws InterruptedException;
|
||||
|
||||
void relightBatch(String taskId, String url, String progress);
|
||||
|
||||
Boolean collectionLikeUpdate(CollectionLikeUpdateDTO collectionLikeUpdateDTO);
|
||||
|
||||
Boolean toProductImageElementDelete(Long id);
|
||||
|
||||
ToProductElementVO convertRelightElement(Long id);
|
||||
|
||||
/**
|
||||
* 根据designItemId获取TO_PRODUCT_IMAGE类型的URL列表和DesignItemDetail的path列表
|
||||
* @param designItemId designItemId
|
||||
* @return 包含TO_PRODUCT_IMAGE类型的URL列表和DesignItemDetail的path列表的对象
|
||||
*/
|
||||
DesignUrlsDTO getToProductImageUrlsByDesignItemId(Long designItemId);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import com.ai.da.common.enums.LoginTypeEnum;
|
||||
import com.ai.da.common.enums.ProductEnum;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.TokenGenerateUtils;
|
||||
import com.ai.da.feign.gateway.GatewayFeignClient;
|
||||
import com.ai.da.feign.seller.SellerFeignClient;
|
||||
import com.ai.da.common.utils.*;
|
||||
import com.ai.da.mapper.primary.*;
|
||||
import com.ai.da.mapper.primary.entity.*;
|
||||
@@ -94,7 +96,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
private AccountExtendMapper accountExtendMapper;
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
private TokenGenerateUtils tokenGenerateUtils;
|
||||
|
||||
@Resource
|
||||
private SellerFeignClient sellerFeignClient;
|
||||
|
||||
@Resource
|
||||
private GatewayFeignClient gatewayFeignClient;
|
||||
|
||||
@Resource
|
||||
private AccountLoginLogService accountLoginLogService;
|
||||
@@ -136,9 +144,6 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Resource
|
||||
private com.ai.da.common.security.config.SecurityProperties securityProperties;
|
||||
|
||||
@Resource
|
||||
private UserFollowService userFollowService;
|
||||
|
||||
@@ -244,12 +249,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
|
||||
response.setEmail(account.getUserEmail());
|
||||
String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
/*if (StringUtils.isNotBlank(token)) {
|
||||
//用户已登入
|
||||
response.setToken(token);
|
||||
} else {
|
||||
response.setToken(createAccountToken(account));
|
||||
}
|
||||
}*/
|
||||
response.setToken(createAccountToken(account));
|
||||
response.setUserId(account.getId());
|
||||
response.setSystemUser(account.getSystemUser());
|
||||
// 设置头像
|
||||
@@ -352,11 +358,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
principal.setUsername(account.getUserName());
|
||||
principal.setLanguage(account.getLanguage());
|
||||
principal.setCountry(account.getCountry());
|
||||
String token2 = jwtTokenHelper.createToken(principal);
|
||||
//区分买家端登录
|
||||
principal.setSource("AIDA");
|
||||
String token2 = tokenGenerateUtils.createToken(principal);
|
||||
// 本地 JVM 缓存(适配旧逻辑)
|
||||
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
|
||||
// 同步写入 Redis,重启后仍然可用
|
||||
long jwtExpiration = securityProperties.getJwtExpiration();
|
||||
long jwtExpiration = tokenGenerateUtils.getJwtExpiration();
|
||||
redisUtil.setLoginToken(account.getId(), token2, jwtExpiration);
|
||||
return token2;
|
||||
}
|
||||
@@ -613,11 +621,23 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
|
||||
@Override
|
||||
public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
|
||||
// jwt 本身失效比较难做,统一用缓存实现:删除缓存即失效
|
||||
// 1. 删除本地缓存(保留,防止 Gateway 未启动时还能本地验证)
|
||||
String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
|
||||
LocalCacheUtils.delTokenCache(userIdStr);
|
||||
// 同时删除 Redis 中的 token,防止服务重启后仍然有效
|
||||
// 2. 删除 Redis 中的 token(Gateway 黑名单会接力生效)
|
||||
redisUtil.deleteLoginToken(accountLogoutDTO.getUserId());
|
||||
// 3. 调用 Gateway 黑名单接口,将 token 加入 Redis 黑名单
|
||||
try {
|
||||
gatewayFeignClient.logout(accountLogoutDTO.getUserId());
|
||||
} catch (Exception e) {
|
||||
log.warn("调用 Gateway 黑名单接口失败,userId={}, error={}", accountLogoutDTO.getUserId(), e.getMessage());
|
||||
}
|
||||
// 4. 同步调用 seller 清除本地缓存(兼容未接入 Gateway 的节点)
|
||||
try {
|
||||
sellerFeignClient.clearTokenCache(accountLogoutDTO.getUserId());
|
||||
} catch (Exception e) {
|
||||
log.warn("调用 seller 清理缓存失败,userId={}, error={}", accountLogoutDTO.getUserId(), e.getMessage());
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@@ -2152,7 +2172,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
redisUtil.addToString(key, newMailbox, CommonConstant.CHANGE_MAILBOX_LINK_VALIDITY / 1000);
|
||||
|
||||
String username = userHolder.getUsername();
|
||||
String token = jwtTokenHelper.createToken(accountId, newMailbox);
|
||||
String token = tokenGenerateUtils.createMailboxToken(accountId, newMailbox);
|
||||
// 准备激活链接,链接应该要有有效期
|
||||
String link = "?" + token;
|
||||
// 向新邮箱发送邮件,邮件附带激活链接,点击链接进行验证
|
||||
@@ -2162,7 +2182,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
// 验证激活链接
|
||||
public void activateNewEmail(String token){
|
||||
// 获取链接地址信息,更新指定用户邮箱
|
||||
String emailAndId = jwtTokenHelper.parseToEmailAndId(token);
|
||||
String emailAndId = tokenGenerateUtils.parseMailboxToken(token);
|
||||
String newMailbox = emailAndId.substring(0, emailAndId.lastIndexOf("_"));
|
||||
String accountId = emailAndId.substring(emailAndId.lastIndexOf("_") + 1);
|
||||
|
||||
|
||||
@@ -520,14 +520,16 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
||||
.filter(f -> f.getDesignType().equals(DesignTypeEnum.COLLECTION.getRealName()))
|
||||
.map(DesignCollectionPrintElementDTO::getId)
|
||||
.collect(Collectors.toList());
|
||||
List<CollectionElement> printBoardElements = new ArrayList<>();
|
||||
elementVO.setPrintBoardElements(printBoardElements);
|
||||
if (!CollectionUtils.isEmpty(printBoardIds)) {
|
||||
// 从数据库批量查询printBoard元素
|
||||
List<CollectionElement> printBoardElements = collectionElementMapper.selectBatchIds(printBoardIds);
|
||||
printBoardElements.addAll(collectionElementMapper.selectBatchIds(printBoardIds));
|
||||
// 验证查询结果的完整性
|
||||
if (CollectionUtil.isEmpty(printBoardElements) || printBoardElements.size() != printBoardIds.size()) {
|
||||
throw new BusinessException("get.printBoards.data.is.mismatch");
|
||||
}
|
||||
elementVO.setPrintBoardElements(printBoardElements);
|
||||
// elementVO.setPrintBoardElements(printBoardElements);
|
||||
usedElementIds.addAll(printBoardIds); // 记录已使用的元素ID
|
||||
}
|
||||
// 处理类型为LIBRARY的printBoard元素
|
||||
@@ -543,7 +545,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
||||
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
||||
libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
||||
printBoardElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
||||
// libraryCollectionElements.addAll(covertLibrarysToPrintCollections(librarys, idToMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +562,8 @@ public class CollectionElementServiceImpl extends ServiceImpl<CollectionElementM
|
||||
Map<Long, DesignCollectionPrintElementDTO> idToMap = designDTO.getPrintBoards()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(DesignCollectionPrintElementDTO::getId, v -> v));
|
||||
generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
||||
printBoardElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
||||
// generateCollectionElements.addAll(covertGeneratesToPrintCollections(generateDetailList, idToMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.xssf.usermodel.*;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -705,14 +704,19 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
||||
|
||||
String username = UserContext.getUserHolder().getUsername();
|
||||
Account account = new Account();
|
||||
account.setId(accountId);
|
||||
// 修改用户有效期截止日期、用户类型、积分
|
||||
if (!Objects.isNull(validEndTime)) {
|
||||
account.setValidEndTime(validEndTime);
|
||||
log.info("管理员:{},修改用户 {} 信息,将账号到期时间置为:{}", username, accountId, validEndTime);
|
||||
}
|
||||
if (!Objects.isNull(systemUser)) {
|
||||
if (!Objects.isNull(systemUser) && !systemUser.equals(0)) {
|
||||
account.setSystemUser(systemUser);
|
||||
log.info("管理员:{},修改用户 {} 信息,将账号身份置为:{}", username, accountId, systemUser);
|
||||
} else if (systemUser.equals(0)){
|
||||
// 将用户身份设置为游客
|
||||
accountService.toVisitor(account);
|
||||
return true;
|
||||
}
|
||||
/*if (!StringUtils.isNullOrEmpty(systemUser)) {
|
||||
int systemUser = 0;
|
||||
@@ -738,7 +742,6 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
||||
}
|
||||
|
||||
// todo 如果修改管理员账号的积分上限或子账号数量,则其所有子账号的积分上限需要重新计算
|
||||
account.setId(accountId);
|
||||
account.setUpdateDate(new Date());
|
||||
return accountMapper.updateById(account) == 1;
|
||||
// accountService.update(account,null);
|
||||
@@ -867,19 +870,20 @@ public class ConvenientInquiryServiceImpl extends ServiceImpl<QuestionnaireMappe
|
||||
if (!StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getOrder()) && queryPaymentInfoDTO.getOrder().equals("ASC")) {
|
||||
order = "ASC";
|
||||
}
|
||||
String status = StringUtil.isNullOrEmpty(queryPaymentInfoDTO.getStatus()) ? "Success" : queryPaymentInfoDTO.getStatus();
|
||||
List<PaymentInfoVO> paymentInfoVOS = paymentInfoMapper.queryPaymentInfo(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
||||
queryPaymentInfoDTO.getType(), status,
|
||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(),
|
||||
size, offset, order, queryPaymentInfoDTO.getPayer());
|
||||
// 查询数据总量
|
||||
Long total = paymentInfoMapper.queryPaymentInfoCount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
||||
queryPaymentInfoDTO.getType(), status,
|
||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
||||
// 查询符合查询条件的总金额
|
||||
BigDecimal payerTotal = paymentInfoMapper.queryTotalPaymentAmount(queryPaymentInfoDTO.getPlatform(), queryPaymentInfoDTO.getPayerTotal(),
|
||||
queryPaymentInfoDTO.getType(), queryPaymentInfoDTO.getStatus(),
|
||||
queryPaymentInfoDTO.getType(), status,
|
||||
queryPaymentInfoDTO.getCountry(), queryPaymentInfoDTO.getCity(),
|
||||
queryPaymentInfoDTO.getStartTime(), queryPaymentInfoDTO.getEndTime(), queryPaymentInfoDTO.getPayer());
|
||||
// 总页数
|
||||
|
||||
@@ -497,6 +497,10 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
|
||||
}
|
||||
|
||||
// 创建 priority 到 DesignSingleItemDTO 的映射,用于获取 transpose 和 rotate
|
||||
Map<Integer, DesignSingleItemDTO> priorityToItemDTOMap = designSingleItemDTOList.stream()
|
||||
.collect(Collectors.toMap(DesignSingleItemDTO::getPriority, dto -> dto, (old, newVal) -> old));
|
||||
|
||||
List<TDesignPythonOutfitDetail> list = new ArrayList<>();
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
JSONObject jsonObject = layers.getJSONObject(i);
|
||||
@@ -525,8 +529,12 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
|
||||
}
|
||||
designPythonOutfitDetail.setPriority(priority);
|
||||
designPythonOutfitDetail.setTranspose(jsonObject.getString("transpose"));
|
||||
designPythonOutfitDetail.setRotate(jsonObject.getDouble("rotate"));
|
||||
// 从前端传入的 DesignSingleItemDTO 中获取 transpose 和 rotate,不再从 Python 返回的数据获取
|
||||
DesignSingleItemDTO itemDTO = priorityToItemDTOMap.get(Math.abs(priority));
|
||||
if (itemDTO != null) {
|
||||
designPythonOutfitDetail.setTranspose(itemDTO.getTranspose() != null ? Arrays.toString(itemDTO.getTranspose()) : null);
|
||||
designPythonOutfitDetail.setRotate(itemDTO.getRotate());
|
||||
}
|
||||
|
||||
list.add(designPythonOutfitDetail);
|
||||
}
|
||||
@@ -1036,7 +1044,7 @@ public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignI
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers, DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||
String designType = "default";
|
||||
String designType = "default";
|
||||
if (Objects.nonNull(designSingleIncludeLayersDTO)) {
|
||||
designType = designSingleIncludeLayersDTO.getDesignType();
|
||||
}
|
||||
|
||||
@@ -756,7 +756,7 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
print.setPosition("[0.0,0.0]");
|
||||
// print.setScale(1d);
|
||||
// todo mark 将print默认scale置为0.3
|
||||
print.setScale(Arrays.toString(new Float[]{0.3f, 0.3f}));
|
||||
print.setScale(Arrays.toString(new Float[]{1.0f, 1.0f}));
|
||||
print.setAngle(0.0);
|
||||
print.setPriority(1);
|
||||
QueryWrapper<CollectionElement> getPrintboardLevel2TypeQw = new QueryWrapper<>();
|
||||
@@ -764,7 +764,20 @@ public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> impleme
|
||||
getPrintboardLevel2TypeQw.lambda().orderByDesc(CollectionElement::getCreateDate);
|
||||
getPrintboardLevel2TypeQw.last("limit 1");
|
||||
CollectionElement one = collectionElementService.getOne(getPrintboardLevel2TypeQw);
|
||||
print.setLevel2Type(one.getLevel2Type());
|
||||
if (Objects.isNull(one)) {
|
||||
QueryWrapper<Library> libraryQueryWrapper = new QueryWrapper<>();
|
||||
libraryQueryWrapper.lambda().eq(Library::getUrl, print.getPath());
|
||||
libraryQueryWrapper.lambda().orderByDesc(Library::getCreateDate);
|
||||
getPrintboardLevel2TypeQw.last("limit 1");
|
||||
Library library = libraryService.getOne(libraryQueryWrapper);
|
||||
if (Objects.isNull(library)) {
|
||||
print.setLevel2Type("Pattern");
|
||||
} else {
|
||||
print.setLevel2Type(library.getLevel2Type());
|
||||
}
|
||||
} else {
|
||||
print.setLevel2Type(one.getLevel2Type());
|
||||
}
|
||||
print.setCreateDate(LocalDateTime.now());
|
||||
designItemDetailPrintService.save(print);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
@@ -59,10 +61,12 @@ import org.bytedeco.javacv.Java2DFrameConverter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
@@ -194,10 +198,13 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
generate.setText(text);
|
||||
Long elementId = generateThroughImageTextDTO.getCollectionElementId();
|
||||
// validateGeneraType(generate, text, elementId);
|
||||
if (!StringUtil.isNullOrEmpty(text)) {
|
||||
text = modifyPrompt(text, generate, generateThroughImageTextDTO.getLevel1Type(), generateThroughImageTextDTO.getAgeGroup());
|
||||
if (!(generateThroughImageTextDTO.getLevel1Type().equals(MOOD_BOARD.getRealName())&&generateThroughImageTextDTO.getModelName().equals("high"))){
|
||||
if (!StringUtil.isNullOrEmpty(text)) {
|
||||
text = modifyPrompt(text, generate, generateThroughImageTextDTO.getLevel1Type(), generateThroughImageTextDTO.getAgeGroup());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// todo 这一步现在还是有必要的吗?
|
||||
// 2.1 sketch或print在t_collection_element表/t_library表中的信息是否需要更新 如 level2Type
|
||||
CollectionElement collectionElement = collectionElementService.editLevel2Type(elementId, generateThroughImageTextDTO.getLevel2Type(), generateThroughImageTextDTO.getDesignType());
|
||||
@@ -218,6 +225,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
version = "fast";
|
||||
params.put("version", "fast");
|
||||
}
|
||||
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
|
||||
saveGenerateImmediately(generate);
|
||||
// 3.1 确定不同类型的印花分别调哪个接口
|
||||
if (generateThroughImageTextDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())) {
|
||||
switch (generateThroughImageTextDTO.getLevel2Type()) {
|
||||
@@ -243,15 +252,28 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
||||
}
|
||||
} else {
|
||||
GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(),
|
||||
mode, category, generateThroughImageTextDTO.getGender(), version);
|
||||
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
||||
if (Objects.equals(version, "fast")) {
|
||||
GenerateToPythonDTO generateToPythonDTO = new GenerateToPythonDTO(generateThroughImageTextDTO.getUniqueId(), text, Objects.isNull(collectionElement) ? "" : collectionElement.getUrl(),
|
||||
mode, category, generateThroughImageTextDTO.getGender(), version);
|
||||
jsonString = JSON.toJSONString(generateToPythonDTO, SerializerFeature.WriteMapNullValue);
|
||||
} else {
|
||||
|
||||
|
||||
path = CommonConstant.GENERATE_PATH_FLUX2_KLEIN;
|
||||
// 构建object_name: {userId}/{category}/{uuid}.png
|
||||
String objectName = generateThroughImageTextDTO.getUserId() + "/" + category + "/" + UUID.randomUUID() + ".png";
|
||||
|
||||
ImageProcessRequest imageProcessRequest = ImageProcessRequest.builder()
|
||||
.object_name(objectName)
|
||||
.bucket_name(userBucket)
|
||||
.prompt(text).build();
|
||||
jsonString = JSON.toJSONString(imageProcessRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path);
|
||||
Boolean requestResult = pythonService.generateSketchOrPrint(jsonString, port, path, generateThroughImageTextDTO.getUniqueId());
|
||||
|
||||
// 4、将请求信息落库,将本次generate的请求信息添加到t_generate表中
|
||||
save(generate);
|
||||
|
||||
// 5、将本次请求存入redis
|
||||
String key = generateResultKey + ":" + generateThroughImageTextDTO.getUniqueId();
|
||||
@@ -266,6 +288,40 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
|
||||
}
|
||||
|
||||
public void saveGenerateImmediately(Generate generate) {
|
||||
save(generate);
|
||||
// 使用 TransactionSynchronizationManager 在事务真正提交后再设锁
|
||||
// 否则 save() 完成后事务尚未 commit,MQ 消费者立即读到 null
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
String lockKey = "generate:lock:" + generate.getUniqueId();
|
||||
redisUtil.addToString(lockKey, "1", 60L);
|
||||
log.debug("Save lock set after commit for uniqueId: {}", generate.getUniqueId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void waitForSaveLock(String uniqueId) {
|
||||
String lockKey = "generate:lock:" + uniqueId;
|
||||
int maxRetries = 30;
|
||||
int retryIntervalMs = 200;
|
||||
for (int i = 0; i < maxRetries; i++) {
|
||||
if (Boolean.TRUE.equals(redisUtil.hasKey(lockKey))) {
|
||||
log.debug("Save lock acquired for uniqueId: {} after {} retries", uniqueId, i);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(retryIntervalMs);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("Interrupted while waiting for save lock: {}", uniqueId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
log.warn("Save lock timeout for uniqueId: {}, proceeding anyway", uniqueId);
|
||||
}
|
||||
|
||||
public GenerateModeEnum getMode(GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||
if (!StringUtil.isNullOrEmpty(generateThroughImageTextDTO.getText())) {
|
||||
if (Objects.nonNull(generateThroughImageTextDTO.getCollectionElementId())) {
|
||||
@@ -284,11 +340,16 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void processGenerateResult(String taskId, String url, String category) {
|
||||
log.info("============ProcessGenerateResult listening==========");
|
||||
log.debug("taskId: " + taskId);
|
||||
String status = null;
|
||||
// 1、处理模型返回的数据
|
||||
GenerateDetail generateDetail = new GenerateDetail();
|
||||
GenerateCollectionItemVO generateCollectionItemVO = new GenerateCollectionItemVO();
|
||||
Generate generate;
|
||||
try {
|
||||
// 等待 HTTP 线程写入完成后再查库
|
||||
waitForSaveLock(taskId);
|
||||
generate = selectByUniqueId(taskId);
|
||||
} catch (MybatisPlusException e) {
|
||||
log.error(e.getMessage());
|
||||
@@ -311,14 +372,15 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
generateDetail.setUrl(url);
|
||||
generateDetail.setGenerateId(generate.getId());
|
||||
generateDetail.setCreateDate(LocalDateTime.now());
|
||||
generateDetail.setMd5(md5);
|
||||
generateDetail.setMd5("");
|
||||
// 将相应的url保存到数据库
|
||||
generateDetailMapper.insert(generateDetail);
|
||||
log.debug("generateDetail: " + generateDetail.toString());
|
||||
|
||||
// String uuid = taskId.substring(0, taskId.substring(0, taskId.lastIndexOf("-")).lastIndexOf("-"));
|
||||
String key = generateResultKey + ":" + taskId;
|
||||
String imageName = url.substring(url.lastIndexOf("/") + 1);
|
||||
String status = imageName.equals("white_image.jpg") ? "Invalid" : "Success";
|
||||
status = imageName.equals("white_image.jpg") ? "Invalid" : "Success";
|
||||
if (StringUtil.isNullOrEmpty(category)) {
|
||||
Generate generateRecord = selectByUniqueId(taskId);
|
||||
category = generateRecord.getLevel2Type();
|
||||
@@ -326,6 +388,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
GenerateResultVO generateResultVO = new GenerateResultVO(taskId, generateDetail.getId(), url, status, category);
|
||||
// 更新redis
|
||||
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
log.debug("generateResultVO: " + generateResultVO.toString());
|
||||
|
||||
|
||||
// 执行积分扣除
|
||||
// ** 注:如果生成的图片都是空白 则不扣积分
|
||||
@@ -785,8 +849,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
long requestEndTime = System.currentTimeMillis();
|
||||
log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}",
|
||||
response.code(), (requestEndTime - requestStartTime), taskId);
|
||||
String result = response.body().string();
|
||||
if (!response.isSuccessful()) {
|
||||
log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId);
|
||||
log.warn("Google API响应失败,状态码: {} for taskId: {},结果:{}", response.code(), taskId, result);
|
||||
if (attempt < maxRetries) {
|
||||
Thread.sleep(retryDelay * attempt); // 递增延迟
|
||||
continue;
|
||||
@@ -795,7 +860,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
}
|
||||
}
|
||||
|
||||
String result = response.body().string();
|
||||
|
||||
// log.info("Google 响应结果:{}", result);
|
||||
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
||||
|
||||
@@ -1065,6 +1130,12 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
|
||||
String result = response.body().string();
|
||||
|
||||
if (response.code() != 200) {
|
||||
log.error("Google API 请求失败 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 响应结果: {}",
|
||||
taskId, attempt, endpoint, response.code(), result);
|
||||
throw new BusinessException("system.error");
|
||||
}
|
||||
|
||||
// log.info("Google 响应结果:{}", result);
|
||||
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result);
|
||||
|
||||
@@ -1203,7 +1274,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
* @param modelName advanced high normal
|
||||
*/
|
||||
private HashMap<String, String> chooseModelAndPrompt(GenerateThroughImageTextDTO generateDTO, String modelName) {
|
||||
if (StringUtil.isNullOrEmpty(modelName)){
|
||||
if (StringUtil.isNullOrEmpty(modelName)) {
|
||||
throw new BusinessException("system error");
|
||||
}
|
||||
HashMap<String, String> modelAndPromptMap = new HashMap<>();
|
||||
@@ -1221,7 +1292,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
String style = generateDTO.getText().substring(0, firstCommaIndex).trim();
|
||||
|
||||
String prompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
|
||||
prompt = getPrintboardPrompt(style, prompt,modelName);
|
||||
prompt = getPrintboardPrompt(style, prompt, modelName, isUseImage);
|
||||
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||
|
||||
|
||||
@@ -1260,8 +1331,31 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
modelAndPromptMap.put(ModelConstants.USE_MODEL, ModelConstants.LOCAL_MODEL);
|
||||
}
|
||||
} else if (ModelConstants.SKETCHBOARD.equals(generateDTO.getLevel1Type())) {
|
||||
String prompt = generateDTO.getText() + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" +
|
||||
String style = "";
|
||||
String userPrompt = "";
|
||||
// 找到第一个逗号的位置
|
||||
int firstCommaIndex = generateDTO.getText().indexOf(",");
|
||||
if (firstCommaIndex != -1) {
|
||||
// 截取第一个逗号前的内容作为style
|
||||
style = generateDTO.getText().substring(0, firstCommaIndex).trim();
|
||||
// 截取第一个逗号后的所有内容作为userPrompt(去除首尾空格)
|
||||
userPrompt = generateDTO.getText().substring(firstCommaIndex + 1).trim();
|
||||
|
||||
if ("Lolita".equals(style)) {
|
||||
style = "洛丽塔";
|
||||
}
|
||||
} else {
|
||||
// 兼容无逗号的情况:style为空,全部内容作为userPrompt
|
||||
userPrompt = generateDTO.getText().trim();
|
||||
}
|
||||
|
||||
String prompt = userPrompt + "rules:front view sketch only,plain white background, single garment only, orthographic, centered on white background, borderless canvas, thin monochrome black line art.\n" +
|
||||
" No clothes hanger, no fake clothes hanger, no human-related lines, no color fill, no words, no text, no black background, no boundary or frame.";
|
||||
|
||||
if (!style.trim().isEmpty() && !"all".equalsIgnoreCase(style)) {
|
||||
prompt += ".sketch style:" + style.trim();
|
||||
}
|
||||
|
||||
modelAndPromptMap.put(ModelConstants.PROMPT, prompt);
|
||||
if (isUseImage) {
|
||||
if (ModelConstants.ADVANCED.equals(modelName)) {
|
||||
@@ -1459,6 +1553,13 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
if (imagePath != null) {
|
||||
requestBuilder.image(finalImagePath1);
|
||||
}
|
||||
if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)|| useModel.equals(ModelConstants.PRINTBOARD_HIGH_T2I)) {
|
||||
GenerateImagesRequest.OptimizePromptOptions optimizePromptOptions = new GenerateImagesRequest.OptimizePromptOptions();
|
||||
optimizePromptOptions.setMode("fast");
|
||||
requestBuilder.optimizePromptOptions(optimizePromptOptions);
|
||||
//由于PRINTBOARD_HIGH_T2I,PRINTBOARD_HIGH_I2I与PRINTBOARD_ADVANCED_I2I使用模型一致,为了区别积分扣除,PRINTBOARD_HIGH_I2I加入了-fast或者-high,但传入模型时需要去掉-fast或者-high,用PRINTBOARD_ADVANCED_I2I的常量做替代
|
||||
requestBuilder.model(ModelConstants.PRINTBOARD_ADVANCED_I2I);
|
||||
}
|
||||
|
||||
// 保存生成记录到数据库
|
||||
Generate generate = new Generate(
|
||||
@@ -1570,7 +1671,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
}
|
||||
|
||||
|
||||
private String getPrintboardPrompt(String style, String userInput, String modelName) {
|
||||
private String getPrintboardPrompt(String style, String userInput, String modelName, boolean isUseImage) {
|
||||
String systemPrompt = null;
|
||||
String prompt;
|
||||
|
||||
@@ -1596,12 +1697,16 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
"Flat textile pattern printed directly on fabric surface, no three-dimensional objects, no items placed on cloth. \n" +
|
||||
"Real style: fabric print, realistic woven/printed pattern, detailed surface pattern only";
|
||||
}
|
||||
}else {
|
||||
throw new BusinessException("style error:"+ style);
|
||||
} else {
|
||||
throw new BusinessException("style error:" + style);
|
||||
}
|
||||
|
||||
if (userInput == null || userInput.trim().isEmpty()) {
|
||||
throw new BusinessException("prompt null");
|
||||
if (isUseImage) {
|
||||
prompt = "Theme: Image content" + "\nRequirement: " + systemPrompt;
|
||||
} else {
|
||||
throw new BusinessException("prompt null");
|
||||
}
|
||||
} else {
|
||||
prompt = "Theme: " + userInput.trim() + "\nRequirement: " + systemPrompt;
|
||||
}
|
||||
@@ -2001,7 +2106,9 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
public Generate selectByUniqueId(String uniqueId) {
|
||||
QueryWrapper<Generate> qw = new QueryWrapper<>();
|
||||
qw.eq("unique_id", uniqueId);
|
||||
|
||||
log.debug("selectByUniqueId: " + uniqueId);
|
||||
Generate one = getOne(qw);
|
||||
log.debug("Generate: " + one);
|
||||
return getOne(qw);
|
||||
}
|
||||
|
||||
@@ -4118,8 +4225,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
}
|
||||
|
||||
// 发送POST请求到Flux API
|
||||
long start = System.currentTimeMillis();
|
||||
String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString());
|
||||
JSONObject respObj = JSONUtil.parseObj(resp);
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("flux 耗时:{}ms", end - start);
|
||||
log.info("flux 发起生成请求返回结果: {}", respObj);
|
||||
|
||||
// 从响应中提取任务ID
|
||||
@@ -4172,11 +4282,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
// 处理不同状态
|
||||
switch (statusEnum) {
|
||||
case TASK_NOT_FOUND:
|
||||
// 审核没过
|
||||
// 审核没过
|
||||
case REQUEST_MODERATED:
|
||||
// 审核没过
|
||||
// 审核没过
|
||||
case CONTENT_MODERATED:
|
||||
// 出错
|
||||
// 出错
|
||||
case ERROR:
|
||||
return "Fail";
|
||||
case PENDING_F:
|
||||
@@ -4295,7 +4405,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
|
||||
MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode());
|
||||
switch (motionModeEnum) {
|
||||
case POSE_TO_VIDEO:
|
||||
params.put("pose_id", poseTransformDTO.getPoseId());
|
||||
params.put("pose_id", poseTransformDTO.getPoseId().toString());
|
||||
params.put("image_url", poseTransformDTO.getProductImage());
|
||||
break;
|
||||
case PROMPT_TO_VIDEO:
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.ai.da.mapper.primary.entity.Notification;
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.dto.PublishSysNotificationDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.model.vo.ContestantCountVO;
|
||||
import com.ai.da.service.GlobalAwardService;
|
||||
import com.ai.da.service.MessageCenterService;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
@@ -22,13 +23,26 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -46,7 +60,7 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
@Value("${file.upload.dir:uploads}")
|
||||
@Value("${file.upload.temp.dir}")
|
||||
private String uploadDir;
|
||||
|
||||
private static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM");
|
||||
@@ -128,6 +142,7 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Map<String, Object> saveContestant(ContestantDTO request) {
|
||||
Map<String,Object> resp = new HashMap<>();
|
||||
if (request.getEmail() == null) {
|
||||
@@ -142,26 +157,60 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (existing == null) {
|
||||
Contestant toInsert = Contestant.builder()
|
||||
.email(request.getEmail())
|
||||
.firstName(request.getFirstName())
|
||||
.lastName(request.getLastName())
|
||||
.gender(request.getGender())
|
||||
.occupation(request.getOccupation())
|
||||
.age(request.getAge())
|
||||
.countryRegionCity(request.getCountryRegionCity())
|
||||
.phoneNumber(request.getPhoneNumber())
|
||||
.designTitle(request.getDesignTitle())
|
||||
.designDescription(request.getDesignDescription())
|
||||
.pdfPath(request.getPdfPath())
|
||||
.videoPath(request.getVideoPath())
|
||||
.createdAt(now)
|
||||
.updatedAt(now)
|
||||
.build();
|
||||
contestantMapper.insert(toInsert);
|
||||
resp.put("success", true);
|
||||
sendSiteMsg(toInsert.getId(), toInsert.getEmail());
|
||||
return resp;
|
||||
// 通过行锁 + 重试机制保证 contestant_number 在并发下自增分配
|
||||
final int maxAttempts = 5;
|
||||
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
// 获取当前最大 contestant_number 并加行锁(LIMIT 1 FOR UPDATE)
|
||||
QueryWrapper<Contestant> qMax = new QueryWrapper<>();
|
||||
qMax.isNotNull("contestant_number");
|
||||
qMax.orderByDesc("contestant_number");
|
||||
qMax.last("LIMIT 1 FOR UPDATE");
|
||||
Contestant last = contestantMapper.selectOne(qMax);
|
||||
Integer nextNumber = (last == null || last.getContestantNumber() == null) ? 10000 : last.getContestantNumber() + 1;
|
||||
|
||||
Contestant toInsert = Contestant.builder()
|
||||
.email(request.getEmail())
|
||||
.firstName(request.getFirstName())
|
||||
.lastName(request.getLastName())
|
||||
.gender(request.getGender())
|
||||
.occupation(request.getOccupation())
|
||||
.age(request.getAge())
|
||||
.countryRegionCity(request.getCountryRegionCity())
|
||||
.phoneNumber(request.getPhoneNumber())
|
||||
.designTitle(request.getDesignTitle())
|
||||
.designDescription(request.getDesignDescription())
|
||||
.pdfPath(request.getPdfPath())
|
||||
.videoPath(request.getVideoPath())
|
||||
.videoDuration(request.getVideoDuration())
|
||||
.videoSize(request.getVideoSize())
|
||||
.pdfSize(request.getPdfSize())
|
||||
.contestantNumber(nextNumber)
|
||||
.portfolioUrl(request.getPortfolioUrl())
|
||||
.createdAt(now)
|
||||
.updatedAt(now)
|
||||
.build();
|
||||
|
||||
contestantMapper.insert(toInsert);
|
||||
|
||||
resp.put("success", true);
|
||||
sendSiteMsg(toInsert.getId(), toInsert.getEmail());
|
||||
return resp;
|
||||
} catch (Exception e) {
|
||||
log.warn("Attempt {} to assign contestant_number failed", attempt, e);
|
||||
String msg = e.getMessage() == null ? "" : e.getMessage().toLowerCase();
|
||||
if ((msg.contains("duplicate") || msg.contains("uniq_contestant_number") || msg.contains("contestant_number")) && attempt < maxAttempts) {
|
||||
try {
|
||||
Thread.sleep(100L);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw new BusinessException("Failed to assign contestant number after retries.");
|
||||
} else {
|
||||
// update existing contestant
|
||||
existing.setFirstName(request.getFirstName());
|
||||
@@ -175,6 +224,10 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
existing.setDesignDescription(request.getDesignDescription());
|
||||
existing.setPdfPath(request.getPdfPath());
|
||||
existing.setVideoPath(request.getVideoPath());
|
||||
existing.setVideoDuration(request.getVideoDuration());
|
||||
existing.setVideoSize(request.getVideoSize());
|
||||
existing.setPdfSize(request.getPdfSize());
|
||||
existing.setPortfolioUrl(request.getPortfolioUrl());
|
||||
existing.setUpdatedAt(now);
|
||||
contestantMapper.updateById(existing);
|
||||
resp.put("success", true);
|
||||
@@ -182,6 +235,132 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportContestants() throws Exception {
|
||||
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
Sheet sheet = workbook.createSheet("contestants");
|
||||
int rowIdx = 0;
|
||||
Row header = sheet.createRow(rowIdx++);
|
||||
String[] headers = new String[] {
|
||||
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
|
||||
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
|
||||
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "portfolioUrl", "createdAt", "updatedAt"
|
||||
};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell c = header.createCell(i);
|
||||
c.setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
for (Contestant cst : list) {
|
||||
Row r = sheet.createRow(rowIdx++);
|
||||
int ci = 0;
|
||||
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
|
||||
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
|
||||
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
|
||||
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
|
||||
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
|
||||
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
|
||||
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
|
||||
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
|
||||
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
|
||||
r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
|
||||
r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
|
||||
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
|
||||
if (cst.getVideoSize() == null) {
|
||||
r.createCell(ci++).setCellValue("");
|
||||
} else {
|
||||
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
|
||||
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
|
||||
}
|
||||
if (cst.getPdfSize() == null) {
|
||||
r.createCell(ci++).setCellValue("");
|
||||
} else {
|
||||
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
|
||||
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
|
||||
}
|
||||
r.createCell(ci++).setCellValue(cst.getPortfolioUrl() == null ? "" : cst.getPortfolioUrl());
|
||||
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
|
||||
}
|
||||
|
||||
workbook.write(out);
|
||||
out.flush();
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
log.error("export contestants failed", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveContestantsToLocal() throws Exception {
|
||||
List<Contestant> list = contestantMapper.selectList(new QueryWrapper<>());
|
||||
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
||||
String ts = LocalDateTime.now().format(fmt);
|
||||
Path exportDir = Paths.get(uploadDir == null ? "uploads" : uploadDir, "exports");
|
||||
Files.createDirectories(exportDir);
|
||||
Path outPath = exportDir.resolve("contestants_" + ts + ".xlsx");
|
||||
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(); FileOutputStream fos = new FileOutputStream(outPath.toFile())) {
|
||||
Sheet sheet = workbook.createSheet("contestants");
|
||||
int rowIdx = 0;
|
||||
Row header = sheet.createRow(rowIdx++);
|
||||
String[] headers = new String[] {
|
||||
"contestantNumber", "email", "firstName", "lastName", "gender", "occupation",
|
||||
"age", "countryRegionCity", "phoneNumber", "designTitle", "designDescription",
|
||||
"pdfPath", "videoPath", "videoDuration", "videoSizeMB", "pdfSizeMB", "portfolioUrl", "createdAt", "updatedAt"
|
||||
};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell c = header.createCell(i);
|
||||
c.setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
for (Contestant cst : list) {
|
||||
Row r = sheet.createRow(rowIdx++);
|
||||
int ci = 0;
|
||||
r.createCell(ci++).setCellValue(cst.getContestantNumber() == null ? "" : cst.getContestantNumber().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getEmail() == null ? "" : cst.getEmail());
|
||||
r.createCell(ci++).setCellValue(cst.getFirstName() == null ? "" : cst.getFirstName());
|
||||
r.createCell(ci++).setCellValue(cst.getLastName() == null ? "" : cst.getLastName());
|
||||
r.createCell(ci++).setCellValue(cst.getGender() == null ? "" : cst.getGender());
|
||||
r.createCell(ci++).setCellValue(cst.getOccupation() == null ? "" : cst.getOccupation());
|
||||
r.createCell(ci++).setCellValue(cst.getAge() == null ? "" : cst.getAge().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getCountryRegionCity() == null ? "" : cst.getCountryRegionCity());
|
||||
r.createCell(ci++).setCellValue(cst.getPhoneNumber() == null ? "" : cst.getPhoneNumber());
|
||||
r.createCell(ci++).setCellValue(cst.getDesignTitle() == null ? "" : cst.getDesignTitle());
|
||||
r.createCell(ci++).setCellValue(cst.getDesignDescription() == null ? "" : cst.getDesignDescription());
|
||||
r.createCell(ci++).setCellValue(cst.getPdfPath() == null ? "" : cst.getPdfPath());
|
||||
r.createCell(ci++).setCellValue(cst.getVideoPath() == null ? "" : cst.getVideoPath());
|
||||
r.createCell(ci++).setCellValue(cst.getVideoDuration() == null ? "" : cst.getVideoDuration().toString());
|
||||
if (cst.getVideoSize() == null) {
|
||||
r.createCell(ci++).setCellValue("");
|
||||
} else {
|
||||
double vMb = cst.getVideoSize() / 1024.0 / 1024.0;
|
||||
r.createCell(ci++).setCellValue(String.format("%.2f", vMb));
|
||||
}
|
||||
if (cst.getPdfSize() == null) {
|
||||
r.createCell(ci++).setCellValue("");
|
||||
} else {
|
||||
double pMb = cst.getPdfSize() / 1024.0 / 1024.0;
|
||||
r.createCell(ci++).setCellValue(String.format("%.2f", pMb));
|
||||
}
|
||||
r.createCell(ci++).setCellValue(cst.getPortfolioUrl() == null ? "" : cst.getPortfolioUrl());
|
||||
r.createCell(ci++).setCellValue(cst.getCreatedAt() == null ? "" : cst.getCreatedAt().toString());
|
||||
r.createCell(ci++).setCellValue(cst.getUpdatedAt() == null ? "" : cst.getUpdatedAt().toString());
|
||||
}
|
||||
|
||||
workbook.write(fos);
|
||||
fos.flush();
|
||||
log.info("Exported contestants to local file: {}", outPath.toString());
|
||||
} catch (IOException e) {
|
||||
log.error("save contestants to local failed", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContestantDTO getContestantByID(String id) {
|
||||
if (id == null) {
|
||||
@@ -204,6 +383,10 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
dto.setDesignDescription(existing.getDesignDescription());
|
||||
dto.setPdfPath(existing.getPdfPath());
|
||||
dto.setVideoPath(existing.getVideoPath());
|
||||
dto.setVideoDuration(existing.getVideoDuration());
|
||||
dto.setPdfSize(existing.getPdfSize());
|
||||
dto.setVideoSize(existing.getVideoSize());
|
||||
dto.setPortfolioUrl(existing.getPortfolioUrl());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -295,6 +478,147 @@ public class GlobalAwardServiceImpl implements GlobalAwardService {
|
||||
// 这里推送消息是在接受到视频生成结束后发生的,所以UserContext中没有用户信息
|
||||
messageCenterService.pushMessage("system", userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportContestantFilesAsZip(Integer minContestantNumber, Integer maxContestantNumber) throws Exception {
|
||||
if (minContestantNumber == null || maxContestantNumber == null) {
|
||||
throw new BusinessException("minContestantNumber and maxContestantNumber are required.");
|
||||
}
|
||||
if (minContestantNumber > maxContestantNumber) {
|
||||
throw new BusinessException("minContestantNumber cannot be greater than maxContestantNumber.");
|
||||
}
|
||||
|
||||
// 1. 根据 contestantNumber 范围查询参赛者
|
||||
QueryWrapper<Contestant> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda()
|
||||
.ge(Contestant::getContestantNumber, minContestantNumber)
|
||||
.le(Contestant::getContestantNumber, maxContestantNumber)
|
||||
.orderByAsc(Contestant::getContestantNumber);
|
||||
List<Contestant> contestants = contestantMapper.selectList(queryWrapper);
|
||||
|
||||
if (contestants.isEmpty()) {
|
||||
log.info("No contestants found in range [{}, {}]", minContestantNumber, maxContestantNumber);
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
// 2. 在内存中构建 ZIP
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(baos)) {
|
||||
|
||||
for (Contestant contestant : contestants) {
|
||||
Integer contestantNumber = contestant.getContestantNumber();
|
||||
if (contestantNumber == null) {
|
||||
log.warn("Contestant {} has no contestantNumber, skipping", contestant.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
String dirPrefix = contestantNumber + "/";
|
||||
|
||||
// 添加 PDF 文件
|
||||
String pdfPath = contestant.getPdfPath();
|
||||
if (StringUtils.isNotBlank(pdfPath)) {
|
||||
addMinioFileToZip(zos, pdfPath, dirPrefix + "design.pdf");
|
||||
}
|
||||
|
||||
// 添加视频文件
|
||||
String videoPath = contestant.getVideoPath();
|
||||
if (StringUtils.isNotBlank(videoPath)) {
|
||||
String fileName = videoPath.contains("/") ?
|
||||
videoPath.substring(videoPath.lastIndexOf("/") + 1) : "video.mp4";
|
||||
addMinioFileToZip(zos, videoPath, dirPrefix + fileName);
|
||||
}
|
||||
|
||||
// 添加参赛者信息 txt 文件
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("=== Contestant Information ===\n\n");
|
||||
sb.append("ID: ").append(nullSafe(contestant.getId())).append("\n");
|
||||
sb.append("Email: ").append(nullSafe(contestant.getEmail())).append("\n");
|
||||
sb.append("Contestant Number: ").append(contestantNumber).append("\n");
|
||||
sb.append("First Name: ").append(nullSafe(contestant.getFirstName())).append("\n");
|
||||
sb.append("Last Name: ").append(nullSafe(contestant.getLastName())).append("\n");
|
||||
sb.append("Gender: ").append(nullSafe(contestant.getGender())).append("\n");
|
||||
sb.append("Occupation: ").append(nullSafe(contestant.getOccupation())).append("\n");
|
||||
sb.append("Age: ").append(contestant.getAge() != null ? contestant.getAge() : "N/A").append("\n");
|
||||
sb.append("Country/Region/City: ").append(nullSafe(contestant.getCountryRegionCity())).append("\n");
|
||||
sb.append("Phone Number: ").append(nullSafe(contestant.getPhoneNumber())).append("\n");
|
||||
sb.append("Design Title: ").append(nullSafe(contestant.getDesignTitle())).append("\n");
|
||||
sb.append("Design Description: ").append(nullSafe(contestant.getDesignDescription())).append("\n");
|
||||
sb.append("PDF Path: ").append(nullSafe(pdfPath)).append("\n");
|
||||
sb.append("PDF Size (bytes): ").append(contestant.getPdfSize() != null ? contestant.getPdfSize() : "N/A").append("\n");
|
||||
sb.append("Video Path: ").append(nullSafe(videoPath)).append("\n");
|
||||
sb.append("Video Duration (seconds): ").append(contestant.getVideoDuration() != null ? contestant.getVideoDuration() : "N/A").append("\n");
|
||||
sb.append("Video Size (bytes): ").append(contestant.getVideoSize() != null ? contestant.getVideoSize() : "N/A").append("\n");
|
||||
sb.append("Portfolio URL: ").append(nullSafe(contestant.getPortfolioUrl())).append("\n");
|
||||
sb.append("Created At: ").append(contestant.getCreatedAt() != null ? contestant.getCreatedAt() : "N/A").append("\n");
|
||||
sb.append("Updated At: ").append(contestant.getUpdatedAt() != null ? contestant.getUpdatedAt() : "N/A").append("\n");
|
||||
|
||||
ZipEntry infoEntry = new ZipEntry(dirPrefix + "contestant_info.txt");
|
||||
zos.putNextEntry(infoEntry);
|
||||
zos.write(sb.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
zos.closeEntry();
|
||||
log.info("Added contestant {} info to zip", contestantNumber);
|
||||
}
|
||||
|
||||
zos.finish();
|
||||
log.info("ZIP built for {} contestants, size: {} bytes", contestants.size(), baos.size());
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 MinIO 文件流式写入 ZIP,不落盘
|
||||
* @param zos ZIP 输出流
|
||||
* @param minioPath MinIO 路径(格式: bucketName/objectPath)
|
||||
* @param entryName ZIP 条目名称
|
||||
*/
|
||||
private void addMinioFileToZip(java.util.zip.ZipOutputStream zos, String minioPath, String entryName) {
|
||||
if (StringUtils.isBlank(minioPath)) {
|
||||
return;
|
||||
}
|
||||
int index = minioPath.indexOf("/");
|
||||
if (index == -1) {
|
||||
log.warn("Invalid MinIO path: {}", minioPath);
|
||||
return;
|
||||
}
|
||||
String bucketName = minioPath.substring(0, index);
|
||||
String objectName = minioPath.substring(index + 1);
|
||||
|
||||
try (InputStream in = minioUtil.download(bucketName, objectName)) {
|
||||
ZipEntry entry = new ZipEntry(entryName);
|
||||
zos.putNextEntry(entry);
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
zos.closeEntry();
|
||||
log.info("Added {} to zip ({} bytes)", entryName, entry.getSize());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to add {} to zip: {}", entryName, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContestantCountVO getContestantCount() {
|
||||
long count = contestantMapper.selectCount(null);
|
||||
Integer maxContestantNumber = null;
|
||||
QueryWrapper<Contestant> qMax = new QueryWrapper<>();
|
||||
qMax.isNotNull("contestant_number");
|
||||
qMax.orderByDesc("contestant_number");
|
||||
qMax.last("LIMIT 1");
|
||||
Contestant last = contestantMapper.selectOne(qMax);
|
||||
if (last != null) {
|
||||
maxContestantNumber = last.getContestantNumber();
|
||||
}
|
||||
return ContestantCountVO.builder()
|
||||
.count(count)
|
||||
.maxContestantNumber(maxContestantNumber)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String nullSafe(String value) {
|
||||
return value != null ? value : "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.ai.da.common.enums.CollectionLevel1TypeEnum;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.PageResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.TokenGenerateUtils;
|
||||
import com.ai.da.common.utils.MinioUtil;
|
||||
import com.ai.da.mapper.primary.*;
|
||||
import com.ai.da.mapper.primary.entity.*;
|
||||
@@ -70,7 +70,7 @@ public class LLMServiceImpl implements LLMService {
|
||||
@Resource
|
||||
private WorkspaceRelStyleMapper workspaceRelStyleMapper;
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
private TokenGenerateUtils tokenGenerateUtils;
|
||||
@Resource
|
||||
private DesignService designService;
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
@@ -89,9 +89,9 @@ public class LLMServiceImpl implements LLMService {
|
||||
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
boolean validate = jwtTokenHelper.validateToken(token); //
|
||||
boolean validate = tokenGenerateUtils.validateToken(token); //
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(token);
|
||||
AuthPrincipalVo principal = tokenGenerateUtils.parserToUser(token);
|
||||
Long accountId = principal.getId();
|
||||
// String url = "http://18.167.251.121:10002/chat-stream";
|
||||
String url = "http://10.1.1.240:1013/chat-stream";
|
||||
@@ -237,10 +237,10 @@ public class LLMServiceImpl implements LLMService {
|
||||
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
boolean validate = jwtTokenHelper.validateToken(token);
|
||||
boolean validate = tokenGenerateUtils.validateToken(token);
|
||||
// boolean validate = true;
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(token);
|
||||
AuthPrincipalVo principal = tokenGenerateUtils.parserToUser(token);
|
||||
Long accountId = principal.getId();
|
||||
// String url = "http://18.167.251.121:10002/api/chat_stream";
|
||||
String url = "http://18.167.251.121:2011/api/chat_stream";
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.netty.util.internal.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -53,7 +54,11 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
||||
private final PythonTAllInfoService pythonTAllInfoService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public LibraryModelPointVO saveOrEditTemplatePoint(LibraryModelPointDTO libraryModelPointDTO) {
|
||||
// 参数校验
|
||||
validateInputParams(libraryModelPointDTO);
|
||||
|
||||
LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(libraryModelPointDTO, LibraryModelPointVO.class);
|
||||
|
||||
// 不管是保存还是另存为,都需要传模特的libraryId
|
||||
@@ -71,7 +76,8 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
||||
// 更新模特图片
|
||||
if (flag) {
|
||||
libModel.setUrl(url);
|
||||
libModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false));
|
||||
String preSignedUrl = minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
|
||||
libModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
|
||||
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
|
||||
libModel.setWidth(imagesWidthAndHeight.get(0));
|
||||
libModel.setHigh(imagesWidthAndHeight.get(1));
|
||||
@@ -104,25 +110,10 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
||||
|
||||
} else {
|
||||
// 不覆盖,即另存为
|
||||
// 新增模特library信息
|
||||
Library saveAsModel = new Library();
|
||||
saveAsModel.setAccountId(libModel.getAccountId());
|
||||
saveAsModel.setLevel1Type(libModel.getLevel1Type());
|
||||
saveAsModel.setLevel2Type(libModel.getLevel2Type());
|
||||
String ageGroup = StringUtil.isNullOrEmpty(libModel.getLevel3Type()) ? "Adult" : libModel.getLevel3Type();
|
||||
saveAsModel.setLevel3Type(ageGroup);
|
||||
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
|
||||
saveAsModel.setUrl(url);
|
||||
saveAsModel.setMd5(MD5Utils.encryptFile(minioUtil.getPreSignedUrl(url, CommonConstant.MINIO_IMAGE_EXPIRE_TIME), false));
|
||||
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(url);
|
||||
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
|
||||
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
|
||||
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
||||
libraryService.save(saveAsModel);
|
||||
// 更新新的模特在library中的id,用于后面新建模特点位信息用
|
||||
libraryModelPointDTO.setLibraryId(saveAsModel.getId());
|
||||
Library saveAsModel = createNewLibraryCopy(libModel, libraryModelPointDTO);
|
||||
|
||||
// 新增模特点位信息
|
||||
libraryModelPointDTO.setLibraryId(saveAsModel.getId()); // 更新libraryId为新创建的模型ID
|
||||
LibraryModelPoint libraryModelPoint = resolvePoint(libraryModelPointDTO);
|
||||
libraryModelPoint.setModelType("Library");
|
||||
libraryModelPoint.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
||||
@@ -130,22 +121,50 @@ public class LibraryModelPointServiceImpl extends ServiceImpl<LibraryModelPointM
|
||||
libraryModelPointVO.setTemplateId(libraryModelPoint.getId());
|
||||
libraryModelPointVO.setRelationId(libraryModelPoint.getRelationId());
|
||||
}
|
||||
//编辑
|
||||
/*if (!StringUtils.isEmpty(libraryModelPointDTO.getModelSex())) {
|
||||
Library byId = libraryService.getById(libraryModelPointDTO.getLibraryId());
|
||||
if (!byId.getLevel2Type().equals(libraryModelPointDTO.getModelSex())) {
|
||||
if (byId.getLevel2Type().equals(Sex.FEMALE.getValue())) {
|
||||
libraryService.checkModel(Sex.FEMALE.getValue(), Collections.singletonList(byId.getId()), 1);
|
||||
}else {
|
||||
libraryService.checkModel(Sex.MALE.getValue(), Collections.singletonList(byId.getId()), 1);
|
||||
}
|
||||
byId.setLevel2Type(libraryModelPointDTO.getModelSex());
|
||||
libraryService.updateById(byId);
|
||||
}
|
||||
}*/
|
||||
return libraryModelPointVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入参数
|
||||
*/
|
||||
private void validateInputParams(LibraryModelPointDTO libraryModelPointDTO) {
|
||||
if (libraryModelPointDTO == null) {
|
||||
throw new BusinessException("libraryModelPointDTO cannot be null");
|
||||
}
|
||||
if (libraryModelPointDTO.getLibraryId() == null || libraryModelPointDTO.getLibraryId() <= 0) {
|
||||
throw new BusinessException("libraryId is required");
|
||||
}
|
||||
if (StringUtils.isEmpty(libraryModelPointDTO.getModelPath())) {
|
||||
throw new BusinessException("modelPath is required");
|
||||
}
|
||||
if (StringUtils.isEmpty(libraryModelPointDTO.getTimeZone())) {
|
||||
throw new BusinessException("timeZone is required");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新的库模型副本
|
||||
*/
|
||||
private Library createNewLibraryCopy(Library originalModel, LibraryModelPointDTO libraryModelPointDTO) {
|
||||
// 新增模特library信息
|
||||
Library saveAsModel = new Library();
|
||||
saveAsModel.setAccountId(originalModel.getAccountId());
|
||||
saveAsModel.setLevel1Type(originalModel.getLevel1Type());
|
||||
saveAsModel.setLevel2Type(originalModel.getLevel2Type());
|
||||
String ageGroup = StringUtil.isNullOrEmpty(originalModel.getLevel3Type()) ? "Adult" : originalModel.getLevel3Type();
|
||||
saveAsModel.setLevel3Type(ageGroup);
|
||||
saveAsModel.setName(DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD));
|
||||
saveAsModel.setUrl(libraryModelPointDTO.getModelPath());
|
||||
String preSignedUrl = minioUtil.getPreSignedUrl(libraryModelPointDTO.getModelPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
|
||||
saveAsModel.setMd5(MD5Utils.encryptFile(preSignedUrl, false));
|
||||
List<Integer> imagesWidthAndHeight = minioUtil.getImagesWidthAndHeight(libraryModelPointDTO.getModelPath());
|
||||
saveAsModel.setWidth(imagesWidthAndHeight.get(0));
|
||||
saveAsModel.setHigh(imagesWidthAndHeight.get(1));
|
||||
saveAsModel.setCreateDate(DateUtil.getByTimeZone(libraryModelPointDTO.getTimeZone()));
|
||||
libraryService.save(saveAsModel);
|
||||
return saveAsModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LibraryModelPointVO saveOrEditTemplatePointOld(LibraryModelPointDTO libraryModelPointDTO) {
|
||||
// Library library = libraryService.getById(libraryModelPointDTO.getLibraryId());
|
||||
|
||||
@@ -31,6 +31,11 @@ public class RabbitMQServiceImpl implements RabbitMQService {
|
||||
mqPublisher.sendGenerateMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishMessageToGenerateResult(String message) {
|
||||
mqPublisher.sendGenerateResultMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishMessageToSR(String message) {
|
||||
mqPublisher.sendSRMessage(message);
|
||||
|
||||
@@ -1060,11 +1060,12 @@ public class StripeServiceImpl implements StripeService {
|
||||
String periodEnd = DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_yyyy_MM_dd_HH_mm_ss);
|
||||
|
||||
qwPI.lambda().eq(PaymentInfo::getOrderNo, subscriptionInfo.getOrderNo())
|
||||
.eq(PaymentInfo::getTradeState, "paid")
|
||||
.between(PaymentInfo::getCreateTime, periodStart, periodEnd)
|
||||
.orderByDesc(PaymentInfo::getId);
|
||||
List<PaymentInfo> paymentInfos = paymentInfoMapper.selectList(qwPI);
|
||||
if (paymentInfos.isEmpty()) {
|
||||
log.info("不发送邮件,原因:【根据order_no:{},查询到的paymentInfos为空】", orderNo);
|
||||
log.info("不发送邮件,原因:【根据order_no:{},查询到的成功的paymentInfos为空】", orderNo);
|
||||
return false;
|
||||
}
|
||||
PaymentInfo paymentInfo = paymentInfos.get(0);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -547,7 +547,7 @@ public class UploadServiceImpl implements UploadService {
|
||||
/**
|
||||
* 清理过期上传任务(每小时执行一次)
|
||||
*/
|
||||
@Scheduled(fixedDelay = 3600000) // 1小时
|
||||
// @Scheduled(fixedDelay = 3600000) // 1小时
|
||||
public void cleanupExpiredUploads() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
uploadTasks.entrySet().removeIf(entry -> {
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
server.port=5567
|
||||
|
||||
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
#spring.datasource.primary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/aida?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.primary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/aida_back?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
#spring.datasource.primary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/test_aida_3.1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.primary.username=aida_con
|
||||
spring.datasource.primary.password=123456
|
||||
|
||||
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.secondary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/attribute_retrieval_style?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.secondary.username=aida_con
|
||||
spring.datasource.secondary.password=123456
|
||||
|
||||
#security
|
||||
spring.security.jwtSecret=JWTSECRET
|
||||
spring.security.jwtTokenHeader=Authorization
|
||||
spring.security.jwtTokenPrefix=Bearer-
|
||||
## 24Сʱ
|
||||
spring.security.jwtExpiration=8640000000
|
||||
#spring security权限设置 认证了token还要认证权限 不然报错Full authentication is required to access this resource
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
rsa.private_key=MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
|
||||
|
||||
#mybatis
|
||||
mybatis-plus.global-config.banner=false
|
||||
mybatis-plus.mapper-locations=classpath:mapper/*/*.xml
|
||||
mybatis-plus.global-config.db-config.logic-delete-field=isDeleted
|
||||
mybatis-plus.global-config.db-config.logic-delete-value=1
|
||||
mybatis-plus.global-config.db-config.logic-not-delete-value=0
|
||||
|
||||
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
|
||||
|
||||
file.mac.path=~/file/
|
||||
file.linux.path=/workspace/home/aida/file/
|
||||
#linux服务器域名(预览和下载用)
|
||||
file.linuxDomain=https://www.aida.com.hk/download/
|
||||
file.windows.path=D:\\upload\\
|
||||
spring.servlet.multipart.max-file-size = 10MB
|
||||
spring.servlet.multipart.max-request-size= 10MB
|
||||
#访问python服务的ip(对应环境)
|
||||
access.python.ip=http://18.167.251.121
|
||||
access.python.port=9994
|
||||
access.python.generate_sr_port=9994
|
||||
access.python.address=http://18.167.251.121:9994
|
||||
|
||||
minio.endpoint=https://www.minio-api.aida.com.hk
|
||||
minio.accessKey=admin
|
||||
minio.secretKey=Aidlab123123!
|
||||
minio.bucketName.clothing=aida-clothing
|
||||
minio.bucketName.mannequins=aida-mannequins
|
||||
minio.bucketName.results=aida-results
|
||||
minio.bucketName.sysImage=aida-sys-image
|
||||
minio.bucketName.users=aida-users
|
||||
minio.bucketName.collectionElement=aida-collection-element
|
||||
minio.bucketName.gradient=aida-gradient
|
||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||
minio.bucketName.slogan=aida-slogan
|
||||
minio.bucketName.partialDesign=aida-partial-design
|
||||
minio.bucketName.globalAward=global-award
|
||||
redirect_url=http://18.167.251.121:7788
|
||||
|
||||
spring.rabbitmq.host=18.167.251.121
|
||||
spring.rabbitmq.port=5672
|
||||
spring.rabbitmq.username=rabbit
|
||||
spring.rabbitmq.password=123456
|
||||
spring.rabbitmq.virtual-host=/
|
||||
|
||||
spring.data.redis.host=172.31.11.32
|
||||
#spring.data.redis.host=18.167.251.121
|
||||
spring.data.redis.port=6379
|
||||
spring.data.redis.database=1
|
||||
spring.data.redis.password=Aidlab
|
||||
spring.data.redis.lettuce.pool.max-active=8
|
||||
spring.data.redis.lettuce.pool.max-idle=8
|
||||
spring.data.redis.lettuce.pool.min-idle=0
|
||||
spring.data.redis.lettuce.pool.max-wait=5
|
||||
|
||||
redis.key.orderForGenerate=OrderForGenerate
|
||||
redis.key.generateCancelSet=GenerateCancelSet
|
||||
redis.key.generateExceptionMap=Generate:Exception
|
||||
redis.key.resultMap=ResultMap
|
||||
redis.key.orderForSR=OrderForSR
|
||||
redis.key.SRCancelSet=SRCancelSet
|
||||
redis.key.SRExceptionMap=SRExceptionMap
|
||||
redis.key.taskList=TaskList
|
||||
redis.key.credits.pre-deduction=Credits:PreDeduction
|
||||
redis.key.generateResult=Generate:Result
|
||||
redis.key.toProductImageResultKey=ToProductImage:Result
|
||||
redis.key.relightResultKey=Relight:Result
|
||||
redis.key.newPosted=LastViewNewPostedTime
|
||||
redis.key.maximumUserId=CodeCreate:MaximumUserId
|
||||
|
||||
aws.s3.accessKeyId=AKIAVD3OJIMF6UJFLSHZ
|
||||
aws.s3.secretKey=LNIwFFB27/QedtZ+Q/viVUoX9F5x1DbuM8N0DkD8
|
||||
aws.s3.regionName=ap-east-1
|
||||
|
||||
# RabbitMQ Exchange and Queue configurations
|
||||
rabbitmq.queues.generate=generate-queue-dev
|
||||
rabbitmq.queues.sr=SR-queue-dev
|
||||
rabbitmq.queues.srResult=SuperResolution-dev
|
||||
rabbitmq.queues.generateResult=GenerateImage-dev
|
||||
rabbitmq.queues.toProductImageResult=ToProductImage-dev
|
||||
rabbitmq.queues.relightResult=Relight-dev
|
||||
rabbitmq.queues.poseTransform=PoseTransform-dev
|
||||
rabbitmq.exchange.generate=generate-exchange
|
||||
rabbitmq.queues.designBatch=DesignBatch-dev
|
||||
rabbitmq.queues.relightBatch=BatchRelight-dev
|
||||
rabbitmq.queues.toProductImageBatch=BatchToProductImage-dev
|
||||
rabbitmq.queues.poseTransformBatch=BatchPoseTransform-dev
|
||||
rabbitmq.queues.emailRetry=emailRetry-business
|
||||
# 死信队列配置
|
||||
rabbitmq.dead-letter.exchange=dlx.email-retry
|
||||
rabbitmq.dead-letter.queue=dlx.email-retry.queue
|
||||
rabbitmq.dead-letter.routing-key=dlx.email-retry.key
|
||||
|
||||
orderList.link=https://develop.aida.com.hk/home/homePage?order=
|
||||
|
||||
# 0 不发送邮件通知 1 发送邮件通知
|
||||
stripe.webhook.fail.reminder=0
|
||||
# kim test
|
||||
#stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG
|
||||
# developer test
|
||||
stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7
|
||||
#thymelea模板配置
|
||||
#控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能
|
||||
spring.thymeleaf.cache=false
|
||||
|
||||
#指定邮件服务器的地址。
|
||||
spring.mail.host=mail.aida.com.hk
|
||||
#指定邮件服务器的端口号。
|
||||
spring.mail.port=465
|
||||
#指定登录邮件服务器的用户名
|
||||
spring.mail.username=info@aida.com.hk
|
||||
#指定登录邮件服务器的密码 / 授权码
|
||||
spring.mail.password=AIdlab@2025
|
||||
spring.mail.default-encoding=UTF-8
|
||||
|
||||
# SSL 配置
|
||||
#启用 SSL 加密
|
||||
spring.mail.properties.mail.smtp.ssl.enable=true
|
||||
#指定 SSL 连接的端口号。通常与 spring.mail.port 一致
|
||||
spring.mail.properties.mail.smtp.socketFactory.port=465
|
||||
|
||||
ALIYUN_API_KEY=sk-dc3f88b7df844fc5a7d3616ebd8a589c
|
||||
DOUBAO_API_KEY=853b3c55-f1dd-406e-a356-64123637f635
|
||||
FREEPIK_API_KEY=FPSX94e5917d376a4facb87dabbaa0319c72
|
||||
|
||||
ollama.url=http://localhost:11434/api/chat
|
||||
|
||||
#google.client.id=29310152396-c44dcsoksjirhn7vbo29p8u8n0sg4qps.apps.googleusercontent.com
|
||||
google.client.id=157095842121-kdd1fdf8m8nudvj9sprstb2k2prnf9e4.apps.googleusercontent.com
|
||||
#google.client.secret=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
|
||||
google.client.secret=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
|
||||
google.redirect.uri=https://develop.api.aida.com.hk/api/third/party/auth/google_callback
|
||||
design.callback.url=https://develop.api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
|
||||
# ===== 分片上传配置 =====
|
||||
|
||||
# 临时文件目录
|
||||
file.upload.temp.dir=temp/uploads
|
||||
|
||||
# 分片大小配置
|
||||
# PDF分片大小:1MB
|
||||
file.upload.chunk.size.pdf=1048576
|
||||
# 视频分片大小:2MB
|
||||
file.upload.chunk.size.video=2097152
|
||||
|
||||
# 文件大小限制
|
||||
# PDF最大文件大小:20MB
|
||||
file.upload.max.size.pdf=20971520
|
||||
# 视频最大文件大小:100MB
|
||||
file.upload.max.size.video=104857600
|
||||
|
||||
# 上传任务过期时间(小时)
|
||||
file.upload.task.expiry.hours=24
|
||||
|
||||
global.award.link=https://develop.aida.com.hk/award/contestants?id=
|
||||
@@ -1,182 +0,0 @@
|
||||
server.port=5567
|
||||
|
||||
#datasource
|
||||
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.primary.jdbcUrl=jdbc:mysql://18.167.251.121:3306/aida?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.primary.username=root
|
||||
spring.datasource.primary.password=QWa998345
|
||||
|
||||
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.secondary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/attribute_retrieval_style?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.secondary.username=aida_con
|
||||
spring.datasource.secondary.password=123456
|
||||
|
||||
#security
|
||||
spring.security.jwtSecret=JWTSECRET
|
||||
spring.security.jwtTokenHeader=Authorization
|
||||
spring.security.jwtTokenPrefix=Bearer-
|
||||
## 24Сʱ
|
||||
#spring.security.jwtExpiration=8640000000
|
||||
spring.security.jwtExpiration=604800000
|
||||
#spring security权限设置 认证了token还要认证权限 不然报错Full authentication is required to access this resource
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR,/api/alipay-hk/**,/api/portfolio/**,\
|
||||
/api/stripe/**,/api/message/**,/api/tags/**,/notification/**,/api/affiliate/**,/api/project/**,/api/llm/**, /api/subscription_plan/**,/api/global-award/**
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
rsa.private_key=MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
|
||||
|
||||
#mybatis
|
||||
mybatis-plus.global-config.banner=false
|
||||
mybatis-plus.mapper-locations=classpath:mapper/*/*.xml
|
||||
mybatis-plus.global-config.db-config.logic-delete-field=isDeleted
|
||||
mybatis-plus.global-config.db-config.logic-delete-value=1
|
||||
mybatis-plus.global-config.db-config.logic-not-delete-value=0
|
||||
|
||||
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
|
||||
|
||||
file.mac.path=~/file/
|
||||
file.linux.path=/workspace/home/aida/file/
|
||||
#linux服务器域名(预览和下载用)
|
||||
file.linuxDomain=https://www.aida.com.hk/download/
|
||||
file.windows.path=D:\\upload\\
|
||||
spring.servlet.multipart.max-file-size = 10MB
|
||||
spring.servlet.multipart.max-request-size= 10MB
|
||||
#访问python服务的ip(对应环境)
|
||||
access.python.ip=http://18.167.251.121
|
||||
access.python.port=9990
|
||||
access.python.generate_sr_port=9990
|
||||
access.python.address=http://18.167.251.121:9990
|
||||
|
||||
minio.endpoint=https://www.minio-api.aida.com.hk
|
||||
minio.accessKey=admin
|
||||
minio.secretKey=Aidlab123123!
|
||||
minio.bucketName.clothing=aida-clothing
|
||||
minio.bucketName.mannequins=aida-mannequins
|
||||
minio.bucketName.results=aida-results
|
||||
minio.bucketName.sysImage=aida-sys-image
|
||||
minio.bucketName.users=aida-users
|
||||
minio.bucketName.collectionElement=aida-collection-element
|
||||
minio.bucketName.gradient=aida-gradient
|
||||
minio.bucketName.modifiedSketch=aida-modified-sketch
|
||||
minio.bucketName.slogan=aida-slogan
|
||||
minio.bucketName.partialDesign=aida-partial-design
|
||||
minio.bucketName.globalAward=global-award
|
||||
redirect_url=http://18.167.251.121:7788
|
||||
|
||||
spring.rabbitmq.host=18.167.251.121
|
||||
spring.rabbitmq.port=5672
|
||||
spring.rabbitmq.username=rabbit
|
||||
spring.rabbitmq.password=123456
|
||||
spring.rabbitmq.virtual-host=/
|
||||
|
||||
spring.data.redis.host=172.31.11.32
|
||||
#spring.data.redis.host=18.167.251.121
|
||||
spring.data.redis.port=6379
|
||||
spring.data.redis.database=2
|
||||
spring.data.redis.password=Aidlab
|
||||
spring.data.redis.lettuce.pool.max-active=8
|
||||
spring.data.redis.lettuce.pool.max-idle=8
|
||||
spring.data.redis.lettuce.pool.min-idle=0
|
||||
spring.data.redis.lettuce.pool.max-wait=5
|
||||
|
||||
redis.key.orderForGenerate=OrderForGenerate
|
||||
redis.key.generateCancelSet=GenerateCancelSet
|
||||
redis.key.generateExceptionMap=Generate:Exception
|
||||
redis.key.resultMap=ResultMap
|
||||
redis.key.orderForSR=OrderForSR
|
||||
redis.key.SRCancelSet=SRCancelSet
|
||||
redis.key.SRExceptionMap=SRExceptionMap
|
||||
redis.key.taskList=TaskList
|
||||
redis.key.credits.pre-deduction=Credits:PreDeduction
|
||||
redis.key.generateResult=Generate:Result
|
||||
redis.key.toProductImageResultKey=ToProductImage:Result
|
||||
redis.key.relightResultKey=Relight:Result
|
||||
redis.key.newPosted=LastViewNewPostedTime
|
||||
redis.key.maximumUserId=CodeCreate:MaximumUserId
|
||||
|
||||
aws.s3.accessKeyId=AKIAVD3OJIMF6UJFLSHZ
|
||||
aws.s3.secretKey=LNIwFFB27/QedtZ+Q/viVUoX9F5x1DbuM8N0DkD8
|
||||
aws.s3.regionName=ap-east-1
|
||||
|
||||
# RabbitMQ Exchange and Queue configurations
|
||||
rabbitmq.queues.generate=generate-queue-prod
|
||||
rabbitmq.queues.sr=SR-queue-prod
|
||||
rabbitmq.queues.srResult=SuperResolution-prod
|
||||
rabbitmq.queues.generateResult=GenerateImage-prod
|
||||
rabbitmq.queues.toProductImageResult=ToProductImage-prod
|
||||
rabbitmq.queues.relightResult=Relight-prod
|
||||
rabbitmq.queues.poseTransform=PoseTransform-prod
|
||||
rabbitmq.exchange.generate=generate-exchange
|
||||
rabbitmq.queues.designBatch=DesignBatch-dev
|
||||
rabbitmq.queues.relightBatch=BatchRelight-dev
|
||||
rabbitmq.queues.toProductImageBatch=BatchToProductImage-dev
|
||||
rabbitmq.queues.poseTransformBatch=BatchPoseTransform-dev
|
||||
rabbitmq.queues.emailRetry=emailRetry-business
|
||||
# 死信队列配置
|
||||
rabbitmq.dead-letter.exchange=dlx.email-retry
|
||||
rabbitmq.dead-letter.queue=dlx.email-retry.queue
|
||||
rabbitmq.dead-letter.routing-key=dlx.email-retry.key
|
||||
|
||||
orderList.link=https://aida.com.hk/home/homePage?order=
|
||||
|
||||
# 0 不发送邮件通知 1 发送邮件通知
|
||||
stripe.webhook.fail.reminder=1
|
||||
# kim live
|
||||
stripe.paymentMethodConfiguration=pmc_1Qu6yJH7nPZ8bkrNULYnFFPf
|
||||
# kim test
|
||||
#stripe.paymentMethodConfiguration=pmc_1LywTWH7nPZ8bkrN6FvdCUWG
|
||||
# developer test
|
||||
#stripe.paymentMethodConfiguration=pmc_1QIKyq02n1TEydyNKVEYvhW7
|
||||
#thymelea模板配置
|
||||
#控制 Thymeleaf 是否启用模板缓存 生产环境用true,以提高性能
|
||||
spring.thymeleaf.cache=false
|
||||
|
||||
#指定邮件服务器的地址。
|
||||
spring.mail.host=mail.aida.com.hk
|
||||
#指定邮件服务器的端口号。
|
||||
spring.mail.port=465
|
||||
#指定登录邮件服务器的用户名
|
||||
spring.mail.username=info@aida.com.hk
|
||||
#指定登录邮件服务器的密码 / 授权码
|
||||
spring.mail.password=AIdlab@2025
|
||||
spring.mail.default-encoding=UTF-8
|
||||
|
||||
# SSL 配置
|
||||
#启用 SSL 加密
|
||||
spring.mail.properties.mail.smtp.ssl.enable=true
|
||||
#指定 SSL 连接的端口号。通常与 spring.mail.port 一致
|
||||
spring.mail.properties.mail.smtp.socketFactory.port=465
|
||||
|
||||
ALIYUN_API_KEY=sk-dc3f88b7df844fc5a7d3616ebd8a589c
|
||||
DOUBAO_API_KEY=853b3c55-f1dd-406e-a356-64123637f635
|
||||
FREEPIK_API_KEY=FPSX94e5917d376a4facb87dabbaa0319c72
|
||||
|
||||
google.client.id=29310152396-nnsd3h533fld665oguu8ovrt1nukmt46.apps.googleusercontent.com
|
||||
google.client.secret=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
|
||||
google.redirect.uri=https://www.api.aida.com.hk/api/third/party/auth/google_callback
|
||||
design.callback.url=https://api.aida.com.hk/api/third/party/receiveDesignResults
|
||||
|
||||
# ===== 分片上传配置 =====
|
||||
|
||||
# 临时文件目录
|
||||
file.upload.temp.dir=temp/uploads
|
||||
|
||||
# 分片大小配置
|
||||
# PDF分片大小:1MB
|
||||
file.upload.chunk.size.pdf=1048576
|
||||
# 视频分片大小:2MB
|
||||
file.upload.chunk.size.video=2097152
|
||||
|
||||
# 文件大小限制
|
||||
# PDF最大文件大小:20MB
|
||||
file.upload.max.size.pdf=20971520
|
||||
# 视频最大文件大小:100MB
|
||||
file.upload.max.size.video=104857600
|
||||
|
||||
# 上传任务过期时间(小时)
|
||||
file.upload.task.expiry.hours=24
|
||||
|
||||
global.award.link=https://www.aida.com.hk/award/contestants?id=
|
||||
@@ -1,101 +0,0 @@
|
||||
server.port=5567
|
||||
|
||||
#datasource
|
||||
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.primary.jdbcUrl=jdbc:mysql://18.167.251.121:3306/aida?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.primary.username=root
|
||||
spring.datasource.primary.password=QWa998345
|
||||
|
||||
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.secondary.jdbcUrl=jdbc:mysql://18.167.251.121:33008/attribute_retrieval_new?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
spring.datasource.secondary.username=aida_con
|
||||
spring.datasource.secondary.password=123456
|
||||
|
||||
#security
|
||||
spring.security.jwtSecret=JWTSECRET
|
||||
spring.security.jwtTokenHeader=Authorization
|
||||
spring.security.jwtTokenPrefix=Bearer-
|
||||
## 24Сʱ
|
||||
spring.security.jwtExpiration=8640000000
|
||||
#spring security权限设置 认证了token还要认证权限 不然报错Full authentication is required to access this resource
|
||||
spring.security.ignorePaths=/,/favicon.ico,/doc.html,/webjars/**,/swagger-resources,/v2/api-docs,\
|
||||
/api/account/**,/api/element/**,/api/python/**,/api/design/**,/api/history/**,/api/library/**,/api/third/party/**,/api/generate/**,/api/workspace/**,/api/classification/**,\
|
||||
/api/product/**,/api/ali-pay/**,/api/order-info/**,/api/paypal/**,/api/credits/**,/api/inquiry/**,/api/tasks/**,/api/python/prepareForSR
|
||||
spring.security.authApi=/auth/login
|
||||
|
||||
|
||||
rsa.private_key=MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
|
||||
|
||||
#mybatis
|
||||
mybatis-plus.global-config.banner=false
|
||||
mybatis-plus.mapper-locations=classpath:mapper/*/*.xml
|
||||
mybatis-plus.global-config.db-config.logic-delete-field=isDeleted
|
||||
mybatis-plus.global-config.db-config.logic-delete-value=1
|
||||
mybatis-plus.global-config.db-config.logic-not-delete-value=0
|
||||
|
||||
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
|
||||
|
||||
file.mac.path=~/file/
|
||||
file.linux.path=/workspace/home/aida/file/
|
||||
#linux服务器域名(预览和下载用)
|
||||
file.linuxDomain=https://www.aida.com.hk/download/
|
||||
file.windows.path=D:\\upload\\
|
||||
spring.servlet.multipart.max-file-size = 10MB
|
||||
spring.servlet.multipart.max-request-size= 10MB
|
||||
#访问python服务的ip(对应环境)
|
||||
#access.python.ip=http://43.198.80.117
|
||||
access.python.ip=http://18.167.251.121
|
||||
#access.python.ip=http://18.167.251.121:9991/
|
||||
access.python.port=9992
|
||||
access.python.sr=http://18.167.251.121:9994
|
||||
|
||||
minio.endpoint=https://www.minio.aida.com.hk:12024
|
||||
minio.accessKey=admin
|
||||
minio.secretKey=Aidlab123123!
|
||||
minio.bucketName.clothing=aida-clothing
|
||||
minio.bucketName.mannequins=aida-mannequins
|
||||
minio.bucketName.results=aida-results
|
||||
minio.bucketName.sysImage=aida-sys-image
|
||||
minio.bucketName.users=aida-users
|
||||
minio.bucketName.collectionElement=aida-collection-element
|
||||
redirect_url=http://18.167.251.121:7788
|
||||
|
||||
spring.rabbitmq.host=18.167.251.121
|
||||
spring.rabbitmq.port=5672
|
||||
spring.rabbitmq.username=rabbit
|
||||
spring.rabbitmq.password=123456
|
||||
spring.rabbitmq.virtual-host=/
|
||||
|
||||
spring.redis.host=172.31.11.32
|
||||
#spring.redis.host=18.167.251.121
|
||||
spring.redis.port=6379
|
||||
spring.redis.database=1
|
||||
spring.redis.password=Aidlab
|
||||
spring.redis.lettuce.pool.max-active=8
|
||||
spring.redis.lettuce.pool.max-idle=8
|
||||
spring.redis.lettuce.pool.min-idle=0
|
||||
spring.redis.lettuce.pool.max-wait=5
|
||||
|
||||
redis.key.orderForGenerate=OrderForGenerate
|
||||
redis.key.generateCancelSet=GenerateCancelSet
|
||||
redis.key.generateExceptionMap=Generate:Exception
|
||||
redis.key.resultMap=ResultMap
|
||||
redis.key.orderForSR=OrderForSR
|
||||
redis.key.SRCancelSet=SRCancelSet
|
||||
redis.key.SRExceptionMap=SRExceptionMap
|
||||
redis.key.taskList=TaskList
|
||||
redis.key.credits.pre-deduction=Credits:PreDeduction
|
||||
redis.key.generateResult=Generate:Result
|
||||
|
||||
aws.s3.accessKeyId=AKIAVD3OJIMF6UJFLSHZ
|
||||
aws.s3.secretKey=LNIwFFB27/QedtZ+Q/viVUoX9F5x1DbuM8N0DkD8
|
||||
aws.s3.regionName=ap-east-1
|
||||
|
||||
# RabbitMQ Exchange and Queue configurations
|
||||
rabbitmq.queues.generate=generate-queue-test
|
||||
rabbitmq.queues.sr=SR-queue-test
|
||||
rabbitmq.queues.srResult=SuperResolution-test
|
||||
rabbitmq.queues.generateResult=GenerateImage-test
|
||||
rabbitmq.queues.toProductImageResult=ToProductImage-test
|
||||
rabbitmq.queues.relightResult=Relight-test
|
||||
rabbitmq.exchange.generate=generate-exchange
|
||||
@@ -1,8 +0,0 @@
|
||||
#<23><><EFBFBD><EFBFBD>application-test<73>ļ<EFBFBD>(<28><><EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>)
|
||||
#spring.profiles.active=test
|
||||
|
||||
#<23><><EFBFBD><EFBFBD>application-prod<6F>ļ<EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
||||
#spring.profiles.active=prod
|
||||
|
||||
#<23><><EFBFBD><EFBFBD>application-dev<65>ļ<EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
|
||||
spring.profiles.active=dev
|
||||
114
src/main/resources/application.yml
Normal file
114
src/main/resources/application.yml
Normal file
@@ -0,0 +1,114 @@
|
||||
# ============================================================
|
||||
# aida-back - 本地配置(不区分环境)
|
||||
# 公共配置(DB、Redis、RabbitMQ、MinIO、API Keys 等)由 Nacos 统一管理
|
||||
# 此文件仅包含 back 服务私有的业务配置
|
||||
# ============================================================
|
||||
|
||||
server:
|
||||
port: 10092
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: aida-back
|
||||
|
||||
|
||||
# ---------- MinIO Buckets ----------
|
||||
minio:
|
||||
bucketName:
|
||||
clothing: aida-clothing
|
||||
mannequins: aida-mannequins
|
||||
results: aida-results
|
||||
sysImage: aida-sys-image
|
||||
users: aida-users
|
||||
collectionElement: aida-collection-element
|
||||
gradient: aida-gradient
|
||||
modifiedSketch: aida-modified-sketch
|
||||
slogan: aida-slogan
|
||||
partialDesign: aida-partial-design
|
||||
globalAward: global-award
|
||||
|
||||
# ---------- Redis Keys ----------
|
||||
redis:
|
||||
key:
|
||||
orderForGenerate: OrderForGenerate
|
||||
generateCancelSet: GenerateCancelSet
|
||||
generateExceptionMap: Generate:Exception
|
||||
resultMap: ResultMap
|
||||
orderForSR: OrderForSR
|
||||
SRCancelSet: SRCancelSet
|
||||
SRExceptionMap: SRExceptionMap
|
||||
taskList: TaskList
|
||||
credits:
|
||||
pre-deduction: Credits:PreDeduction
|
||||
generateResult: Generate:Result
|
||||
toProductImageResultKey: ToProductImage:Result
|
||||
relightResultKey: Relight:Result
|
||||
newPosted: LastViewNewPostedTime
|
||||
maximumUserId: CodeCreate:MaximumUserId
|
||||
|
||||
# ---------- RabbitMQ 队列 ----------
|
||||
rabbitmq:
|
||||
queues:
|
||||
generate: generate-queue
|
||||
sr: SR-queue
|
||||
srResult: SuperResolution
|
||||
generateResult: GenerateImage
|
||||
toProductImageResult: ToProductImage
|
||||
relightResult: Relight
|
||||
poseTransform: PoseTransform
|
||||
designBatch: DesignBatch
|
||||
relightBatch: BatchRelight
|
||||
toProductImageBatch: BatchToProductImage
|
||||
poseTransformBatch: BatchPoseTransform
|
||||
emailRetry: emailRetry-business
|
||||
exchange:
|
||||
generate: generate-exchange
|
||||
dead-letter:
|
||||
exchange: dlx.email-retry
|
||||
queue: dlx.email-retry.queue
|
||||
routing-key: dlx.email-retry.key
|
||||
|
||||
# ---------- 第三方服务 ----------
|
||||
orderList:
|
||||
link: https://develop.aida.com.hk/home/homePage?order=
|
||||
|
||||
stripe:
|
||||
webhook:
|
||||
fail:
|
||||
reminder: 0
|
||||
paymentMethodConfiguration: pmc_1QIKyq02n1TEydyNKVEYvhW7
|
||||
|
||||
google:
|
||||
client:
|
||||
id: 157095842121-kdd1fdf8m8nudvj9sprstb2k2prnf9e4.apps.googleusercontent.com
|
||||
secret: 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
|
||||
redirect:
|
||||
uri: https://develop.api.aida.com.hk/api/third/party/auth/google_callback
|
||||
|
||||
redirect:
|
||||
url: http://18.167.251.121:7788
|
||||
|
||||
global:
|
||||
award:
|
||||
link: https://aida-global-design-awards.com.hk/contestants?id=
|
||||
|
||||
# ---------- 文件上传 ----------
|
||||
file:
|
||||
upload:
|
||||
temp:
|
||||
dir: temp/uploads
|
||||
chunk:
|
||||
size:
|
||||
pdf: 1048576
|
||||
video: 2097152
|
||||
max:
|
||||
size:
|
||||
pdf: 20971520
|
||||
video: 104857600
|
||||
task:
|
||||
expiry:
|
||||
hours: 24
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.aida: debug
|
||||
34
src/main/resources/bootstrap.yml
Normal file
34
src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
# ============================================================
|
||||
# aida-back - Bootstrap
|
||||
# 通过 NACOS_NAMESPACE 环境变量切换命名空间(dev / test / prod)
|
||||
# 示例:docker run -e NACOS_NAMESPACE=prod ...
|
||||
# ============================================================
|
||||
|
||||
nacos:
|
||||
namespace: dev
|
||||
host: 18.167.251.121:28848
|
||||
username: nacos
|
||||
password: Aidlab123123!
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: aida-back
|
||||
config:
|
||||
import: optional:nacos:aida-public-${nacos.namespace}.yml
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${nacos.host}
|
||||
namespace: ${nacos.namespace}
|
||||
username: ${nacos.username}
|
||||
password: ${nacos.password}
|
||||
# ip: 18.167.251.121
|
||||
port: 10092
|
||||
# ip-type: ipv4
|
||||
# prefer-ip-address: true
|
||||
config:
|
||||
server-addr: ${nacos.host}
|
||||
namespace: ${nacos.namespace}
|
||||
file-extension: yaml
|
||||
username: ${nacos.username}
|
||||
password: ${nacos.password}
|
||||
@@ -17,3 +17,5 @@
|
||||
|
||||
</mapper>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ waistbandRight.cannot.be.empty=waistbandRight cannot be empty.
|
||||
handLeft.cannot.be.empty=handLeft cannot be empty.
|
||||
handRight.cannot.be.empty=handRight cannot be empty.
|
||||
id.cannot.be.empty=id cannot be empty.
|
||||
url.cannot.be.empty=url cannot be empty.
|
||||
type.cannot.be.empty=type cannot be empty.
|
||||
color.cannot.be.empty=color cannot be empty.
|
||||
generateDetailId.cannot.be.empty=generateDetailId cannot be empty.
|
||||
|
||||
@@ -110,6 +110,7 @@ waistbandRight.cannot.be.empty=waistbandRight不能为空。
|
||||
handLeft.cannot.be.empty=handLeft不能为空。
|
||||
handRight.cannot.be.empty=handRight不能为空。
|
||||
id.cannot.be.empty=id不能为空。
|
||||
url.cannot.be.empty=url不能为空。
|
||||
type.cannot.be.empty=type不能为空。
|
||||
color.cannot.be.empty=color不能为空。
|
||||
generateDetailId.cannot.be.empty=generateDetailId不能为空。
|
||||
|
||||
Reference in New Issue
Block a user