28 Commits

Author SHA1 Message Date
litianxiang
921d2d956e minio缓存 2026-05-20 15:09:26 +08:00
litianxiang
d700f94f9d flux test 2026-05-14 16:36:55 +08:00
litianxiang
b277479e73 豆包模型更换 2026-05-13 20:52:28 +08:00
litianxiang
83cbd57dea 登录鉴权按照Source判断id来自于何处 2026-05-13 09:40:30 +08:00
litianxiang
4d3b22de82 买家端需要的获取商家主页和模糊搜索接口 2026-05-11 16:40:44 +08:00
litianxiang
6b5c2cfec0 日志
订单表字段改名
视频返回新增字段
2026-05-06 16:58:46 +08:00
litianxiang
b676de054a 新工作流 2026-05-06 14:37:42 +08:00
litianxiang
4c169ef67e seller产品图处加入视频 2026-05-06 13:30:21 +08:00
litianxiang
f2bce066b6 seller产品图编辑 2026-05-06 10:46:14 +08:00
litianxiang
6af442eb15 服务端口号与宿主机统一,方便本地调试不需要修改bootstrap 2026-05-04 14:21:35 +08:00
litianxiang
768df55309 服务端口号与宿主机统一,方便本地调试不需要修改bootstrap 2026-05-04 14:19:37 +08:00
litianxiang
f351277b73 nacos注册配置 2026-05-04 10:27:20 +08:00
litianxiang
a799162ea4 工作流 2026-04-28 16:40:56 +08:00
litianxiang
c035eb9d7d bootstrap配置 2026-04-28 14:38:00 +08:00
litianxiang
906a54b3c8 工作流配置 2026-04-28 13:58:46 +08:00
litianxiang
643799546b 工作流配置 2026-04-28 13:42:24 +08:00
litianxiang
f582464cd3 工作流配置 2026-04-28 13:37:47 +08:00
litianxiang
b864b393bc 1 2026-04-28 13:21:12 +08:00
litianxiang
c03a8762e7 商品bug 2026-04-28 09:39:26 +08:00
litianxiang
cb87ad1099 host配置 2026-04-27 16:43:18 +08:00
litianxiang
fb229764f8 host配置 2026-04-27 16:29:54 +08:00
litianxiang
8bec1f842d 工作流配置 2026-04-27 16:10:01 +08:00
litianxiang
b54bd04cff 端口号冲突 2026-04-27 15:00:28 +08:00
litianxiang
b4ccad6242 服务名修改 2026-04-27 14:30:29 +08:00
litianxiang
6068bf7d7d 微服务改造 2026-04-27 13:54:03 +08:00
litianxiang
d36baf747f 微服务改造 2026-04-27 11:47:15 +08:00
7c8f1bee6a 添加 .gitea/workflows/develop_3.1_MS_build_manual.yaml 2026-04-24 13:40:03 +08:00
litianxiang
62bd145e2c 微服务改造 2026-04-23 10:52:49 +08:00
28 changed files with 4559 additions and 4002 deletions

View 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

View File

@@ -103,7 +103,13 @@ jobs:
- ./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内容:"

View File

@@ -263,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>

View File

@@ -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) {

View File

@@ -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");
// }
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -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天过期

View File

@@ -18,8 +18,8 @@ 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 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";

View File

@@ -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();
}
}

View File

@@ -28,7 +28,7 @@ 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();
@@ -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();
}

View File

@@ -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()){

View File

@@ -104,7 +104,7 @@ public class PaymentTask {
}
// 定时同步(每分钟一次)
@Scheduled(fixedRate = 60000)
// @Scheduled(fixedRate = 60000)
public void syncLinkViewCountToDB(){
affiliateService.syncLinkViewCountToDB();
}
@@ -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();

View File

@@ -1,171 +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 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);
}
}
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);
}
}

View File

@@ -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客户端实例
*/
@@ -48,6 +52,18 @@ public class MinioUtil {
return minioClient;
}
@Autowired
private RedisUtil redisUtil;
/**
* Redis缓存key前缀用于Minio签名URL缓存
*/
private static final String REDIS_MINIO_URL_PREFIX = "minio:url:";
/**
* 签名URL缓存过期时间默认1天
*/
private static final long URL_CACHE_EXPIRE_SECONDS = 24 * 60 * 60;
/**
* description: 判断bucket是否存在不存在则创建
*
@@ -388,6 +404,11 @@ public class MinioUtil {
* @return 文件的临时URL如果出现异常则返回null
*/
public String getPreSignedUrl(String bucketName, String fileName, int expiry) {
String cacheKey = REDIS_MINIO_URL_PREFIX + bucketName + "/" + fileName;
Object cachedUrl = redisUtil.getFromString(cacheKey);
if (cachedUrl != null) {
return cachedUrl.toString();
}
try {
String lowerName = fileName.toLowerCase();
@@ -415,8 +436,9 @@ public class MinioUtil {
builder.extraQueryParams(queryParams);
}
return minioClient.getPresignedObjectUrl(builder.build());
String presignedObjectUrl = minioClient.getPresignedObjectUrl(builder.build());
redisUtil.addToString(cacheKey, presignedObjectUrl, URL_CACHE_EXPIRE_SECONDS);
return presignedObjectUrl;
} catch (MinioException | InvalidKeyException
| IOException | NoSuchAlgorithmException | IllegalArgumentException e) {
e.printStackTrace();
@@ -958,6 +980,166 @@ public class MinioUtil {
}
}
/**
* 检测字符串是否为预签名URL
* 通过检查URL中是否包含minio endpoint来判断
*
* @param str 待检测的字符串
* @return true表示是预签名URLfalse表示不是
*/
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或逻辑路径");
}
}
}

View File

@@ -77,7 +77,7 @@ public class PythonService {
@Value("${access.python.generate_sr_port}")
private String srServicePort;
@Value("${design.callback.url}")
@Value("${design.callback.url.aida}")
private String callbackUrl;
@Resource

View 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;
}

View File

@@ -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;
}

View 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);
}
}

View File

@@ -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);
}

View File

@@ -358,6 +358,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
principal.setUsername(account.getUserName());
principal.setLanguage(account.getLanguage());
principal.setCountry(account.getCountry());
//区分买家端登录
principal.setSource("AIDA");
String token2 = tokenGenerateUtils.createToken(principal);
// 本地 JVM 缓存(适配旧逻辑)
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);

View File

@@ -1553,11 +1553,11 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
if (imagePath != null) {
requestBuilder.image(finalImagePath1);
}
if (useModel.equals(ModelConstants.PRINTBOARD_HIGH_I2I)) {
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_I2I与PRINTBOARD_ADVANCED_I2I使用模型一致为了区别积分扣除PRINTBOARD_HIGH_I2I加入了-fast但传入模型时需要去掉-fast用PRINTBOARD_ADVANCED_I2I的常量做替代
//由于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);
}
@@ -4225,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

View File

@@ -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 -> {

View File

@@ -5,28 +5,12 @@
# ============================================================
server:
port: 5567
port: 10092
spring:
application:
name: aida-back
# ---------- 副数据源back 私有,由 Nacos 统一管理) ----------
# ---------- Token 生成参数(由 TokenGenerateUtils 使用) ----------
security:
jwtSecret: JWTSECRET
jwtTokenHeader: Authorization
jwtTokenPrefix: Bearer-
jwtExpiration: 8640000000
# ---------- Python 服务 ----------
access:
python:
ip: http://18.167.251.121
port: 9994
generate_sr_port: 9994
address: http://18.167.251.121:9994
# ---------- MinIO Buckets ----------
minio:
@@ -101,10 +85,6 @@ google:
redirect:
uri: https://develop.api.aida.com.hk/api/third/party/auth/google_callback
design:
callback:
url: https://darkish-copied-sprinkler.ngrok-free.dev/api/third/party/receiveDesignResults
redirect:
url: http://18.167.251.121:7788

View File

@@ -4,18 +4,31 @@
# 示例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:test}.yml
import: optional:nacos:aida-public-${nacos.namespace}.yml
cloud:
nacos:
discovery:
server-addr: ${NACOS_HOST:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:test}
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:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:test}
group: ${NACOS_GROUP:DEFAULT_GROUP}
server-addr: ${nacos.host}
namespace: ${nacos.namespace}
file-extension: yaml
username: ${nacos.username}
password: ${nacos.password}

View File

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

View File

@@ -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不能为空。