diff --git a/src/main/java/com/ai/da/common/task/PaymentTask.java b/src/main/java/com/ai/da/common/task/PaymentTask.java index cd441e2e..4999af54 100644 --- a/src/main/java/com/ai/da/common/task/PaymentTask.java +++ b/src/main/java/com/ai/da/common/task/PaymentTask.java @@ -87,9 +87,9 @@ public class PaymentTask { // !!关闭此定时器,改为提前三天站内信提醒!! // 提前7天向用户发送提醒邮件,每天早上8点执行 // @Scheduled(cron = "0 0 8 * * ?") - public void subscriptionReminder(){ - stripeService.subscriptionReminder(); - } +// public void subscriptionReminder(){ +// stripeService.subscriptionReminder(); +// } // 如果有订阅已创建,但是没有发邮件通知的,需要主动获取回调信息并向用户发送邮件 diff --git a/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java new file mode 100644 index 00000000..a29838d6 --- /dev/null +++ b/src/main/java/com/ai/da/common/task/SubscriptionReminderTask.java @@ -0,0 +1,129 @@ +package com.ai.da.common.task; + +import com.ai.da.common.utils.SendEmailUtil; +import com.ai.da.mapper.primary.AccountMapper; +import com.ai.da.mapper.primary.SubscriptionInfoMapper; +import com.ai.da.mapper.primary.entity.Account; +import com.ai.da.mapper.primary.entity.SubscriptionInfo; +import com.ai.da.service.StripeService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@RequiredArgsConstructor +public class SubscriptionReminderTask { + + public final AccountMapper accountMapper; + + public final SubscriptionInfoMapper subscriptionInfoMapper; + + public final StripeService stripeService; + + // 订阅类型与提前天数的映射配置 + private static final Map REMINDER_DAYS_CONFIG = new HashMap<>(); + + static { + REMINDER_DAYS_CONFIG.put("monthly", 7); + REMINDER_DAYS_CONFIG.put("yearly", 14); + } + + public void subscriptionReminder() { + // 获取所有需要通知的订阅 + List subscriptionInfos = getDueSubscriptions(); + + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + Integer daysBefore = REMINDER_DAYS_CONFIG.get(subscriptionInfo.getType()); + if (daysBefore == null) { + log.warn("未知的订阅类型: {}", subscriptionInfo.getType()); + continue; + } + + boolean success = stripeService.sendEmail(subscriptionInfo.getSubscriptionId(), "reminder_subscriber", null); + if (success) { + log.info("提前{}天向用户 {} 发送续订通知邮件,订阅类型: {}", + daysBefore, subscriptionInfo.getAccountId(), subscriptionInfo.getType()); + } + } + } + + private List getDueSubscriptions() { + List results = new ArrayList<>(); + + for (Map.Entry entry : REMINDER_DAYS_CONFIG.entrySet()) { + String subscriptionType = entry.getKey(); + int daysBefore = entry.getValue(); + + results.addAll(getSubscriptionsDueInDays(subscriptionType, daysBefore)); + } + + return results; + } + + private List getSubscriptionsDueInDays(String subscriptionType, int daysBefore) { + LocalDateTime targetDate = LocalDateTime.now().plusDays(daysBefore); + LocalDateTime startOfDay = targetDate.toLocalDate().atStartOfDay(); + LocalDateTime endOfDay = targetDate.toLocalDate().atTime(23, 59, 59); + + long startTimestamp = startOfDay.toEpochSecond(ZoneOffset.UTC); + long endTimestamp = endOfDay.toEpochSecond(ZoneOffset.UTC); + + QueryWrapper qw = new QueryWrapper<>(); + qw.ge("current_period_end", startTimestamp); + qw.lt("current_period_end", endTimestamp); + qw.eq("status", "active"); + qw.eq("subscription_type", subscriptionType); + + return subscriptionInfoMapper.selectList(qw); + } + + public void trialReminder() { + // 今天的 00:00:00 和 23:59:59 + LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay(); + LocalDateTime endOfDay = LocalDateTime.now().toLocalDate().atTime(23, 59, 59); + + // 转为时间戳 + long startTimestamp = startOfDay.toEpochSecond(ZoneOffset.UTC); + long endTimestamp = endOfDay.toEpochSecond(ZoneOffset.UTC); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.lambda().gt(Account::getValidEndTime, startTimestamp) + .lt(Account::getValidEndTime, endTimestamp) + .eq(Account::getSystemUser, 3); + + List accounts = accountMapper.selectList(queryWrapper); + for (Account account : accounts) { + String language = stripeService.getLanguage(account.getLanguage(), account.getCountry(), "reminder_trial"); + SendEmailUtil.subscriptionEmailReminder("reminder_trial", null, language, account.getUserEmail()); + } + } + + +/* public void subscriptionReminder(){ + // 提前7天的 00:00:00 和 23:59:59 + LocalDateTime startOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atStartOfDay(); + LocalDateTime endOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atTime(23, 59, 59); + + // 转为时间戳 + long startTimestamp = startOfDay.toEpochSecond(ZoneOffset.UTC); + long endTimestamp = endOfDay.toEpochSecond(ZoneOffset.UTC); + + QueryWrapper qw = new QueryWrapper<>(); + qw.ge("current_period_end", startTimestamp); + qw.lt("current_period_end", endTimestamp); + qw.eq("status", "active"); + + List subscriptionInfos = subscriptionInfoMapper.selectList(qw); + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), "reminder", null); + if (b) log.info("提前7天向用户 {} 发送续订通知邮件", subscriptionInfo.getAccountId()); + } + }*/ +} diff --git a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java index 9ed81d98..fd49e25c 100644 --- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java @@ -828,15 +828,31 @@ public class SendEmailUtil { templateUser.setTemplateID(RENEWAL_USER_CN); } break; - case "reminder": + case "reminder_subscriber": if (language.equals("ENGLISH")) { user.setSubject("[Code-Create] AiDA Subscription Renewal Reminder"); - templateUser.setTemplateID(RENEWAL_REMINDER_USER_EN); - } else { + templateUser.setTemplateID(156072L); + } else if (language.equals("CHINESE")){ user.setSubject("[Code-Create] AiDA续订提醒"); - templateUser.setTemplateID(RENEWAL_REMINDER_USER_CN); + templateUser.setTemplateID(156073L); + } else { + user.setSubject("[Code-Create] AiDA續訂提醒"); + templateUser.setTemplateID(156074L); } break; + case "reminder_trial": + if (language.equals("ENGLISH")) { + user.setSubject("[Code-Create] AiDA — Free Trial Ending"); + templateUser.setTemplateID(156075L); + } else if (language.equals("CHINESE")){ + user.setSubject("[Code-Create] AiDA — 免费试用结束提醒"); + templateUser.setTemplateID(156076L); + } else { + user.setSubject("[Code-Create] AiDA — 免費試用結束提醒"); + templateUser.setTemplateID(156077L); + } + break; + default: log.error("unknown subscription email type"); return false; @@ -855,7 +871,7 @@ public class SendEmailUtil { SendEmailResponse respUser = client.SendEmail(user); log.info("邮件主题:{},发送结果toUser###{}", user.getSubject(), SendEmailResponse.toJsonString(respUser)); } - if (!type.equals("reminder")) { + if (!type.startsWith("reminder")) { SendEmailResponse respMerchant = client.SendEmail(merchant); log.info("邮件主题:{},发送结果toMerchant###{}", merchant.getSubject(), SendEmailResponse.toJsonString(respMerchant)); } diff --git a/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java b/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java index c704b637..fa6ae844 100644 --- a/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java +++ b/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java @@ -29,7 +29,7 @@ public class PoseTransformDTO { @ApiModelProperty("design结果在collectionSort中的id") private Long parentId; - @ApiModelProperty("子集中的元素作为父元素") + @ApiModelProperty("子集中的元素作为父元素,用于重新排序") private Long userLikeSortId; @ApiModelProperty("是否默认喜欢") @@ -39,5 +39,5 @@ public class PoseTransformDTO { private String prompt; @ApiModelProperty("生成模式 1.pose2video | 2.prompt2video | 3.FLFrame2video ") - private int mode; + private Integer mode; } diff --git a/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java index b6064d5f..0af473d1 100644 --- a/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java +++ b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java @@ -61,5 +61,5 @@ public class SubscriptionEmailParamsDTO { // 付款失败原因 private String failMessage; - + private String days; } diff --git a/src/main/java/com/ai/da/service/MessageCenterService.java b/src/main/java/com/ai/da/service/MessageCenterService.java index 50b7574f..206f481a 100644 --- a/src/main/java/com/ai/da/service/MessageCenterService.java +++ b/src/main/java/com/ai/da/service/MessageCenterService.java @@ -27,4 +27,6 @@ public interface MessageCenterService extends IService { void setReadAll(String type); void publishSystemNotification(PublishSysNotificationDTO message); + + void videoFinishedMsg(Long userId, String projectName, boolean isSuccess); } diff --git a/src/main/java/com/ai/da/service/StripeService.java b/src/main/java/com/ai/da/service/StripeService.java index 673bc530..23fe2010 100644 --- a/src/main/java/com/ai/da/service/StripeService.java +++ b/src/main/java/com/ai/da/service/StripeService.java @@ -37,11 +37,13 @@ public interface StripeService { boolean sendEmail(String subscriptionId, String type, String orderNo); + String getLanguage(String language, String country, String type); + /*void updateSubscription(String subscriptionId); void resume(String subscriptionId);*/ - void subscriptionReminder(); +// void subscriptionReminder(); void checkSubscriptionExpiration(); diff --git a/src/main/java/com/ai/da/service/impl/EmailServiceImpl.java b/src/main/java/com/ai/da/service/impl/EmailServiceImpl.java index 155e36a0..4bb9a677 100644 --- a/src/main/java/com/ai/da/service/impl/EmailServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/EmailServiceImpl.java @@ -634,7 +634,7 @@ public class EmailServiceImpl implements EmailService { userTemplate = RENEWAL_USER_CN; } break; - case "reminder": + /*case "reminder": if (language.equals("ENGLISH")) { userSubject = "[Code-Create] AiDA Subscription Renewal Reminder"; userTemplate = RENEWAL_REMINDER_USER_EN; @@ -642,6 +642,30 @@ public class EmailServiceImpl implements EmailService { userSubject = "[Code-Create] AiDA续订提醒"; userTemplate = RENEWAL_REMINDER_USER_CN; } + break;*/ + case "reminder_subscriber": + if (language.equals("ENGLISH")) { + userSubject = "[Code-Create] AiDA Subscription Renewal Reminder"; + userTemplate = String.valueOf(156072L); + } else if (language.equals("CHINESE")){ + userSubject = "[Code-Create] AiDA续订提醒"; + userTemplate = String.valueOf(156073L); + } else { + userSubject = "[Code-Create] AiDA續訂提醒"; + userTemplate = String.valueOf(156074L); + } + break; + case "reminder_trial": + if (language.equals("ENGLISH")) { + userSubject = "[Code-Create] AiDA — Free Trial Ending"; + userTemplate = String.valueOf(156075L); + } else if (language.equals("CHINESE")){ + userSubject = "[Code-Create] AiDA — 免费试用结束提醒"; + userTemplate = String.valueOf(156076L); + } else { + userSubject = "[Code-Create] AiDA — 免費試用結束提醒"; + userTemplate = String.valueOf(156077L); + } break; default: log.error("unknown subscription email type"); @@ -654,7 +678,7 @@ public class EmailServiceImpl implements EmailService { sendEmail(Collections.singletonList(receiverAddress), jsonObject, userTemplate, userSubject, null, null); } // 排除不向商家发送邮件的情况 - if (!type.equals("reminder")) { + if (!type.startsWith("reminder")) { sendEmail(merchantReceiver, jsonObject, merchantTemplate, merchantSubject, null, null); } return true; diff --git a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java index 93c7cbc1..0e407587 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -44,6 +44,7 @@ import com.volcengine.ark.runtime.model.images.generation.ImagesResponse; import com.volcengine.ark.runtime.service.ArkService; import io.minio.errors.MinioException; import io.netty.util.internal.StringUtil; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.apache.commons.io.IOUtils; @@ -84,58 +85,35 @@ import static com.ai.da.common.enums.WangXiangTaskStatusEnum.UNKNOWN_W; @Slf4j @Service +@RequiredArgsConstructor public class GenerateServiceImpl extends ServiceImpl implements GenerateService { - @Resource - private CollectionElementMapper collectionElementMapper; - @Resource - private GenerateDetailMapper generateDetailMapper; - @Resource - private LibraryService libraryService; - @Resource - private PythonService pythonService; - @Resource - private CollectionElementService collectionElementService; - @Resource - private CreditsService creditsService; - @Resource - private MinioUtil minioUtil; - @Resource - private RabbitMQService rabbitMQService; - @Resource - private RedisUtil redisUtil; - @Resource - private GenerateCancelMapper generateCancelMapper; - @Resource - private SketchReconstructionMapper sketchReconstructionMapper; - @Resource - private SendRequestUtil sendRequestUtil; - @Resource - private ProjectService projectService; - @Resource - private CollectionSortService collectionSortService; - @Resource - private CloudTaskService cloudTaskService; - @Resource - private APIGenerateMapper apiGenerateMapper; - @Resource - private CollectionSortMapper collectionSortMapper; - @Resource - private SysFileService sysFileService; - @Resource - private LibraryModelPointService libraryModelPointService; - @Resource - private PoseTransformationMapper poseTransformationMapper; - @Resource - private CloudTaskMapper cloudTaskMapper; - @Resource - private ToProductImageResultMapper toProductImageResultMapper; - @Resource - private AccountService accountService; - @Resource - private APIGenerateService apiGenerateService; - @Resource - private UserLikeGroupService userLikeGroupService; + private final CollectionElementMapper collectionElementMapper; + private final GenerateDetailMapper generateDetailMapper; + private final LibraryService libraryService; + private final PythonService pythonService; + private final CollectionElementService collectionElementService; + private final CreditsService creditsService; + private final MinioUtil minioUtil; + private final RabbitMQService rabbitMQService; + private final RedisUtil redisUtil; + private final GenerateCancelMapper generateCancelMapper; + private final SketchReconstructionMapper sketchReconstructionMapper; + private final SendRequestUtil sendRequestUtil; + private final ProjectService projectService; + private final CollectionSortService collectionSortService; + private final CloudTaskService cloudTaskService; + private final APIGenerateMapper apiGenerateMapper; + private final CollectionSortMapper collectionSortMapper; + private final SysFileService sysFileService; + private final LibraryModelPointService libraryModelPointService; + private final PoseTransformationMapper poseTransformationMapper; + private final CloudTaskMapper cloudTaskMapper; + private final ToProductImageResultMapper toProductImageResultMapper; + private final AccountService accountService; + private final APIGenerateService apiGenerateService; + private final UserLikeGroupService userLikeGroupService; + private final MessageCenterService messageCenterService; @Value("${redis.key.orderForGenerate}") private String consumptionOrderKey; @@ -2472,6 +2450,9 @@ public class GenerateServiceImpl extends ServiceImpl i } public ToProductImageResultVO poseTransform(PoseTransformDTO poseTransformDTO) { + // 参数判断 + handleMotionParams(poseTransformDTO); + Long accountId = UserContext.getUserHolder().getId(); Long projectId = poseTransformDTO.getProjectId(); String productImage = poseTransformDTO.getProductImage(); @@ -2557,11 +2538,28 @@ public class GenerateServiceImpl extends ServiceImpl i throw new BusinessException("pose.transformation.error", ResultEnum.ERROR.getCode()); } + private void handleMotionParams(PoseTransformDTO poseTransformDTO) { + if (Objects.isNull(poseTransformDTO.getMode()) || poseTransformDTO.getMode() == 0) { + throw new BusinessException("Mode cannot be empty"); + } + MotionModeEnum motionModeEnum = MotionModeEnum.of(poseTransformDTO.getMode()); + if (Objects.isNull(motionModeEnum)) { + throw new BusinessException("unknown.mode"); + } + if (poseTransformDTO.getMode() == 1 && Objects.isNull(poseTransformDTO.getPoseId())) { + throw new BusinessException("Please choose a pose"); + } + if (poseTransformDTO.getMode() == 3 && StringUtil.isNullOrEmpty(poseTransformDTO.getLastFrameProductImage())) { + throw new BusinessException("Last frame cannot be empty"); + } + } + + private CollectionSort addPoseTransferLike(PoseTransformDTO poseTransformDTO, Long poseTransformationId) { if (Objects.nonNull(poseTransformDTO.getParentId()) && !poseTransformDTO.getParentId().equals(0L)) { return disOrLikePose(poseTransformationId, "like", - poseTransformDTO.getProjectId(), poseTransformDTO.getUserLikeSortId()); + poseTransformDTO.getProjectId(), poseTransformDTO.getParentId()); } return null; } @@ -2599,6 +2597,12 @@ public class GenerateServiceImpl extends ServiceImpl i // String uuid = taskId.substring(0, taskId.lastIndexOf("-")); Boolean flag = creditsService.taskCreditsDeduction(Long.parseLong(accountId), taskId); if (flag) creditsService.updateChangedCredits(accountId, taskId); + + Project project = projectService.getById(poseTransformation.getProjectId()); + // 发通知 + if (Objects.nonNull(project) && !StringUtil.isNullOrEmpty(project.getName())) { + messageCenterService.videoFinishedMsg(poseTransformation.getAccountId(), project.getName() , true); + } } public List getPoseTransformationResult(List taskIdList, Long projectId, Boolean like) { diff --git a/src/main/java/com/ai/da/service/impl/MessageCenterServiceImpl.java b/src/main/java/com/ai/da/service/impl/MessageCenterServiceImpl.java index ed3e042f..d8d231ef 100644 --- a/src/main/java/com/ai/da/service/impl/MessageCenterServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/MessageCenterServiceImpl.java @@ -17,6 +17,7 @@ import com.ai.da.mapper.primary.entity.Portfolio; import com.ai.da.mapper.primary.entity.SysNotificationReadStatus; import com.ai.da.model.dto.GetNotificationDTO; import com.ai.da.model.dto.PublishSysNotificationDTO; +import com.ai.da.model.enums.Language; import com.ai.da.model.vo.NotificationVO; import com.ai.da.service.AccountService; import com.ai.da.service.MessageCenterService; @@ -97,9 +98,18 @@ public class MessageCenterServiceImpl extends ServiceImpl wrapper + .isNull(Notification::getReceiverId) + .or() + .eq(Notification::getReceiverId, accountId) + ); + } else { + queryWrapper.lambda().eq(Notification::getReceiverId, accountId); } + Page notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper); List unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId); @@ -378,4 +388,31 @@ public class MessageCenterServiceImpl extends ServiceImpl