12 Commits

Author SHA1 Message Date
litianxiang
9c68ce74ac Merge remote-tracking branch 'origin/dev/3.1_release_merge_MS' into dev/3.1_release_merge_MS 2026-06-10 17:54:54 +08:00
litianxiang
5745c334df rabbit监听queues修改 2026-06-10 17:54:46 +08:00
d9c0e67c07 task:开启消息监听 2026-06-10 17:38:26 +08:00
fe9cc99701 TASK:站内信格式调整 2026-06-03 18:32:12 +08:00
73c366d827 TASK:卖家审核通知 2026-06-03 17:55:15 +08:00
litianxiang
85e02a895c 去掉视频给卖家端fix 2026-06-02 17:07:35 +08:00
litianxiang
148bb84f3c 去掉视频给卖家 2026-06-02 15:02:45 +08:00
litianxiang
931eef6f53 登录黑名单清除失效问题 2026-05-29 16:02:03 +08:00
litianxiang
3d9a6aa9e9 适配无token接口的报错拦截器 2026-05-29 15:42:47 +08:00
11073690e5 TASK:允许修改订阅结束时间到今天之后的日期 2026-05-22 10:35:57 +08:00
litianxiang
921d2d956e minio缓存 2026-05-20 15:09:26 +08:00
litianxiang
d700f94f9d flux test 2026-05-14 16:36:55 +08:00
15 changed files with 257 additions and 105 deletions

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

@@ -44,9 +44,11 @@ public class ControllerLoggingAspect {
// 获取当前用户ID
Long userId = null;
try {
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
if (authPrincipalVo != null) {
userId = authPrincipalVo.getId();
} catch (RuntimeException e) {
// 匿名接口,无认证上下文,忽略
}
// 获取请求参数
@@ -121,9 +123,11 @@ public class ControllerLoggingAspect {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Long userId = null;
try {
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
if (authPrincipalVo != null) {
userId = authPrincipalVo.getId();
} catch (RuntimeException e) {
// 匿名接口,无认证上下文,忽略
}
// 获取请求参数

View File

@@ -52,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是否存在不存在则创建
*
@@ -392,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();
@@ -419,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();

View File

@@ -1076,4 +1076,45 @@ public class SendEmailUtil {
}
private final static Long SELLER_APPROVED = 184414L;
private final static Long SELLER_REJECTED = 184415L;
public static void sellerApproval(String receiver, boolean isApproved) {
try {
// 实例化一个认证对象
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ses.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
SendEmailRequest req = new SendEmailRequest();
req.setFromEmailAddress(CODE_CREATE_SEND_ADDRESS);
req.setDestination(new String[]{receiver});
// 根据邮件类型设置不同的主题和模板
String subject;
Template template = new Template();
if (isApproved) {
subject = "AiDA卖家权限已开通 AiDA Seller Access Enabled";
template.setTemplateID(SELLER_APPROVED);
}else {
subject = "AiDA卖家权限审批不通过 Seller Access Not Approved";
template.setTemplateID(SELLER_REJECTED);
}
req.setSubject(subject);
req.setTemplate(template);
// 发送邮件
SendEmailResponse resp = client.SendEmail(req);
log.info("邮件发送成功,收件人地址:{}", receiver);
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
log.info(receiver);
log.error("邮件发送失败###{},收件人地址:{}", e.toString(), receiver);
}
}
}

View File

@@ -6,6 +6,7 @@ import com.ai.da.model.dto.GetNotificationDTO;
import com.ai.da.model.vo.NotificationVO;
import com.ai.da.model.dto.PublishSysNotificationDTO;
import com.ai.da.service.MessageCenterService;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@@ -60,4 +61,12 @@ public class MessageCenterController {
messageCenterService.setReadAll(type);
return Response.success("success");
}
@Hidden
@Operation(summary = "卖家审批结果站内信通知")
@PostMapping("/sellerApprovalNotice")
public Response<String> sellerApprovalNotice(@RequestParam("userId") Long userId, @RequestParam("isApproved") boolean isApproved) {
messageCenterService.sellerApprovalNotice(userId, isApproved);
return Response.success("success");
}
}

View File

@@ -18,4 +18,10 @@ public interface GatewayFeignClient {
*/
@PostMapping("/logout")
Response<Void> logout(@RequestParam("userId") Long userId);
/**
* 清除用户黑名单,允许该用户重新登录(登录时会自动调用)。
*/
@PostMapping("/clear-blacklist")
Response<Void> clearBlacklist(@RequestParam("userId") Long userId);
}

View File

@@ -29,4 +29,6 @@ public interface MessageCenterService extends IService<Notification> {
void publishSystemNotification(PublishSysNotificationDTO message);
void videoFinishedMsg(Long userId, String projectName, boolean isSuccess);
void sellerApprovalNotice(Long userId, boolean isApproved);
}

View File

@@ -366,6 +366,12 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
// 同步写入 Redis重启后仍然可用
long jwtExpiration = tokenGenerateUtils.getJwtExpiration();
redisUtil.setLoginToken(account.getId(), token2, jwtExpiration);
// 清除黑名单,允许用户重新登录(仅当黑名单功能开启时)
try {
gatewayFeignClient.clearBlacklist(account.getId());
} catch (Exception e) {
log.warn("登录时清除黑名单失败userId={}, error={}", account.getId(), e.getMessage());
}
return token2;
}

View File

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

@@ -7,6 +7,7 @@ import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.MinioUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.common.websocket.NotificationConnection;
import com.ai.da.mapper.primary.*;
import com.ai.da.mapper.primary.entity.*;
@@ -441,4 +442,50 @@ public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, No
pushMessage("system", userId);
}
private final static String APPROVED_MESSAGE = "尊敬的用户,您的卖家权限已开通。" +
"现在可通过\"成为卖家\"的同一入口进入卖家中心。\n在卖家中心中您可以" +
"\n·从设计项目中批量选择服装设计并创建上架内容 " +
"\n·将设计及高级工具媒体转为可售卖的数字商品 " +
"\n·编辑、保存、发布并管理商品状态" +
"\n\nDear User, your seller access has been enabled. " +
"You can now enter the Seller Dashboard from the same entry point used to become a seller.\nIn the Seller Dashboard, you can:" +
"\n·Batch select apparel designs from a design project and create listings" +
"\n·Turn designs and Advanced Tools media into sellable digital items " +
"\n·Edit, save, publish, and manage item status";
private final static String REJECTED_MESSAGE = "尊敬的用户,您的卖家权限申请审批未通过。 请检查您提交的信息,并确保您的卖家资料符合平台要求。您可以更新相关信息后重新提交申请。\n\n" +
"Dear User, your seller access request was not approved. Please review the information you submitted and make sure your seller profile meets the platform requirements. You may update the relevant information and resubmit your application.";
public void sellerApprovalNotice(Long userId, boolean isApproved) {
if (userId != null && userId != 0) {
PublishSysNotificationDTO sysNotificationDTO = new PublishSysNotificationDTO();
Notification notification = new Notification();
notification.setType("system");
notification.setReceiverId(userId);
if (isApproved) {
sysNotificationDTO.setTitle("卖家权限审批通过 Seller Access Enabled");
sysNotificationDTO.setContent(APPROVED_MESSAGE);
} else {
sysNotificationDTO.setTitle("卖家权限审批不通过 Seller Access Not Approved");
sysNotificationDTO.setContent(REJECTED_MESSAGE);
}
notification.setContent(JSON.toJSONString(sysNotificationDTO));
notification.setIsRead(0);
notification.setCreateTime(LocalDateTime.now());
// 保存消息内容
save(notification);
// 推送系统消息
pushMessage("system", userId);
Account account = accountService.getById(userId);
if (account != null) {
// 发送邮件
SendEmailUtil.sellerApproval(account.getUserEmail(), isApproved);
}
}
}
}

View File

@@ -169,7 +169,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
}
/**
* 处理结束时间(只能延长)
* 处理结束时间,不允许订阅结束时间早于当前时间和订阅开始时间
*/
private void handlePeriodEnd(UpdateSubscriptionPlanDTO dto, SubscriptionPlan plan) {
Long newEnd = dto.getCurrentPeriodEnd();
@@ -177,9 +177,20 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMap
return;
}
if (newEnd < plan.getCurrentPeriodEnd()) {
long currentTimeSec = System.currentTimeMillis() / 1000;
long startTime = plan.getCurrentPeriodStart();
// 检查是否早于开始时间不能等于否则周期长度为0
if (newEnd <= startTime) {
throw new BusinessException(
"the.subscription.end.date.can.be.extended.only.not.reduced"
"end.time.cannot.be.earlier.than.or.equal.to.start.time"
);
}
// 检查是否早于当前时间(不能等于,否则立即过期)
if (newEnd <= currentTimeSec) {
throw new BusinessException(
"end.time.cannot.be.earlier.than.or.equal.to.the.current.time"
);
}

View File

@@ -92,14 +92,15 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
for (CollectionSort userLikeSort : childSortList) {
if (userLikeSort.getRelationType().equals(CollectionType.POSE_TRANSFORM.getValue())){
PoseTransformation poseTransformation = poseTransformationMapper.selectById(userLikeSort.getRelationId());
if (poseTransformation != null) {
PoseTransformationVideoDTO videoDTO = new PoseTransformationVideoDTO();
videoDTO.setFirstFrameUrl(minioUtil.processMinioResource(poseTransformation.getFirstFrameUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
videoDTO.setGifUrl(minioUtil.processMinioResource(poseTransformation.getGifUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
videoDTO.setVideoUrl(minioUtil.processMinioResource(poseTransformation.getVideoUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
designUrlsDTO.getVideos().add(videoDTO);
}
//2026.6.2 不显示视频到卖家端
// PoseTransformation poseTransformation = poseTransformationMapper.selectById(userLikeSort.getRelationId());
// if (poseTransformation != null) {
// PoseTransformationVideoDTO videoDTO = new PoseTransformationVideoDTO();
// videoDTO.setFirstFrameUrl(minioUtil.processMinioResource(poseTransformation.getFirstFrameUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
// videoDTO.setGifUrl(minioUtil.processMinioResource(poseTransformation.getGifUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
// videoDTO.setVideoUrl(minioUtil.processMinioResource(poseTransformation.getVideoUrl(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
// designUrlsDTO.getVideos().add(videoDTO);
// }
}else {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (toProductImageResult != null && !isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())) {

View File

@@ -49,17 +49,17 @@ redis:
# ---------- 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
generate: generate-queue-dev
sr: SR-queue-dev
srResult: SuperResolution-dev
generateResult: GenerateImage-dev
toProductImageResult: ToProductImage-dev
relightResult: Relight-dev
poseTransform: PoseTransform-dev
designBatch: DesignBatch-dev
relightBatch: BatchRelight-dev
toProductImageBatch: BatchToProductImage-dev
poseTransformBatch: BatchPoseTransform-dev
emailRetry: emailRetry-business
exchange:
generate: generate-exchange

View File

@@ -211,6 +211,8 @@ please.specify.the.organizationId=Please specify the organizationId.
switch.failed.sub-account.not.under.your.active.subscription=Switch failed. Sub-account not under your active subscription.
Sub-accounts.cannot.be.admins=Sub-accounts in a subscription cannot be designated as admins.
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=Only subscription plans with a PENDING status can have their start time modified.
end.time.cannot.be.earlier.than.or.equal.to.start.time=End time cannot be earlier than or equal to start time.
end.time.cannot.be.earlier.than.or.equal.to.the.current.time=End time cannot be earlier than or equal to the current time.
the.subscription.end.date.can.be.extended.only.not.reduced=The subscription end date can be extended only, not reduced.
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=Total sub-account quota cannot be lower than existing sub-accounts.
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=The credit limit set cannot be lower than the amount of credits already used.

View File

@@ -207,6 +207,8 @@ please.specify.the.organizationId=请指定organizationId
switch.failed.sub-account.not.under.your.active.subscription=切换失败,该子账号不属于您当前管理的订阅计划
Sub-accounts.cannot.be.admins=在订阅中的子账号不能被指定为管理员
only.subscription.plans.with.a.PENDING.status.can.have.their.start.time.modified=只有PENDING状态的订阅计划可以修改订阅开始时间
end.time.cannot.be.earlier.than.or.equal.to.start.time=订阅结束时间不能早于或等于开始时间
end.time.cannot.be.earlier.than.or.equal.to.the.current.time=订阅结束时间不能早于或等于当前时间
the.subscription.end.date.can.be.extended.only.not.reduced=订阅的到期时间不能缩短,只能延长
total.sub-account.quota.cannot.be.lower.than.existing.sub-accounts=设置的子账号总数量不能低于现存已添加的子账号数量
the.credit.limit.set.cannot.be.lower.than.the.amount.of.credits.already.used=设置的积分上限不能低于已使用的积分量