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..44ae1778 100644 --- a/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java +++ b/src/main/java/com/ai/da/model/dto/PoseTransformDTO.java @@ -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/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..cffff034 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -2472,6 +2472,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,6 +2560,23 @@ 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)) { diff --git a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java index a580b0ed..373942e0 100644 --- a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java @@ -16,6 +16,7 @@ import com.ai.da.model.dto.CreateCouponDTO; import com.ai.da.model.dto.ProductPurchaseDTO; import com.ai.da.model.dto.QueryCouponsPageDTO; import com.ai.da.model.dto.SubscriptionEmailParamsDTO; +import com.ai.da.model.enums.Language; import com.ai.da.model.vo.CheckCouponsVO; import com.ai.da.service.*; import com.alibaba.fastjson.JSON; @@ -1072,7 +1073,7 @@ public class StripeServiceImpl implements StripeService { String key = RedisUtil.SUBSCRIPTION_SENT_EMAIL_TYPE + subscriptionInfo.getId(); // 先判断当前订单 这个类型的邮件是否已发送过 Boolean elementExistsInSet = redisUtil.isElementExistsInSet(key, type); - if (!type.equals("reminder") && !type.equals("cancel") && paymentInfo.getNotified() == 1 && elementExistsInSet){ + if (!type.startsWith("reminder") && !type.equals("cancel") && paymentInfo.getNotified() == 1 && elementExistsInSet){ // 已经邮件通知过,直接返回 log.info("不发送邮件,原因:【type为:{},order_no为:{},已经进行邮件通知】", type, orderNo); return true; @@ -1080,7 +1081,7 @@ public class StripeServiceImpl implements StripeService { com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(subscriptionInfo.getAccountId()); String userName = account.getUserName(); - String language = account.getLanguage(); + String language = getLanguage(account.getLanguage(), account.getCountry(), type); OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(subscriptionInfo.getOrderNo()); SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO(); @@ -1101,7 +1102,7 @@ public class StripeServiceImpl implements StripeService { if (!b) return false; // 邮件通知成功后,更新标志 - if (!type.equals("reminder") && !type.equals("cancel")){ + if (!type.startsWith("reminder") && !type.equals("cancel")){ PaymentInfo payment = new PaymentInfo(); payment.setId(paymentInfo.getId()); payment.setNotified(1); @@ -1114,6 +1115,20 @@ public class StripeServiceImpl implements StripeService { return true; } + public String getLanguage(String language, String country, String type) { + if (StringUtil.isNullOrEmpty(language)){ + return Language.ENGLISH.name(); + } else { + if (!StringUtil.isNullOrEmpty(type) && type.startsWith("reminder") + && language.equals(Language.CHINESE_SIMPLIFIED.name()) + && !StringUtil.isNullOrEmpty(country) + && (country.equals("Hong Kong, China") || country.equals("Taiwan, China"))) { + return "zh-Hant"; + } + return language; + } + } + public boolean sendEmail(String orderNo){ SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO(); OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo); @@ -1257,9 +1272,10 @@ public class StripeServiceImpl implements StripeService { emailParamsDTO.setNextPayDate(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy)); } emailParamsDTO.setRenewalTime(DateUtil.changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy)); + emailParamsDTO.setDays(subscriptionInfo.getType().equals("month") ? "7" : subscriptionInfo.getType().equals("year") ? "14" : "N/A"); } - public void subscriptionReminder(){ + /*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); @@ -1278,7 +1294,7 @@ public class StripeServiceImpl implements StripeService { boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), "reminder", null); if (b) log.info("提前7天向用户 {} 发送续订通知邮件", subscriptionInfo.getAccountId()); } - } + }*/ public void checkSubscriptionExpiration(){ long epochSecond = Instant.now().getEpochSecond();