diff --git a/src/main/java/com/ai/da/common/constant/CommonConstant.java b/src/main/java/com/ai/da/common/constant/CommonConstant.java index e495ee26..cd019602 100644 --- a/src/main/java/com/ai/da/common/constant/CommonConstant.java +++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java @@ -79,5 +79,7 @@ public class CommonConstant { public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy"; + public static final String AFFILIATE_LINK = ""; + } diff --git a/src/main/java/com/ai/da/common/utils/RedisUtil.java b/src/main/java/com/ai/da/common/utils/RedisUtil.java index 7d5295dc..d3ba2a93 100644 --- a/src/main/java/com/ai/da/common/utils/RedisUtil.java +++ b/src/main/java/com/ai/da/common/utils/RedisUtil.java @@ -278,4 +278,18 @@ public class RedisUtil { // 设置过期时间为 5 分钟(300 秒) redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES); } + + public final static String PAYMENT_INFO_LAST_SCAN_TIME = "PaymentInfoLastScanTime:"; + + public final static String AFFILIATE_LINK_VIEW_KEY = "AffiliateLink:view:"; + + public void increaseAffiliateLinkViewCount(Long accountId) { + String key = AFFILIATE_LINK_VIEW_KEY + accountId; + redisTemplate.opsForValue().increment(key); + } + + public Long getAffiliateLinkViewCount(Long accountId) { + String key = AFFILIATE_LINK_VIEW_KEY + accountId; + return redisTemplate.opsForValue().increment(key, 0); + } } 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 b14cd28a..490b598f 100644 --- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java +++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java @@ -2,6 +2,7 @@ package com.ai.da.common.utils; import com.ai.da.mapper.primary.entity.Account; import com.ai.da.mapper.primary.entity.TrialOrder; +import com.ai.da.model.dto.AffiliateEmailParamsDTO; import com.ai.da.model.dto.SubscriptionEmailParamsDTO; import com.alibaba.fastjson.JSONObject; import com.ai.da.common.config.exception.BusinessException; @@ -177,7 +178,7 @@ public class SendEmailUtil { subject = "Approval Confirmation for AiDA System Trial Access"; if (country.equals("China")) { template.setTemplateID(NOTIFICATION_CHINESE_TEMPLATE_ID); - }else { + } else { template.setTemplateID(NOTIFICATION_TEMPLATE_ID); } template.setTemplateData(buildNotificationData(trialOrder, link)); @@ -227,7 +228,7 @@ public class SendEmailUtil { attachment.setFileName(fileName); // 设置附件文件名 // 设置附件内容 attachment.setContent(Base64.getEncoder().encodeToString(fileContent)); - req.setAttachments(new Attachment[] {attachment}); + req.setAttachments(new Attachment[]{attachment}); // 发送邮件 SendEmailResponse resp = client.SendEmail(req); log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp)); @@ -270,7 +271,9 @@ public class SendEmailUtil { throw new BusinessException("failed.to.send.mail"); } } + private final static Long WILLBEEXPIRED_TEMPLATE_ID = 118178L; + public static void sendWillBeExpiredEmail(Account account, String senderAddress) { try { // 实例化一个认证对象 @@ -362,7 +365,7 @@ public class SendEmailUtil { jsonObject.put("email", trialOrder.getEmail()); if (link) { jsonObject.put("days", 14); - }else { + } else { jsonObject.put("days", 5); } return jsonObject.toJSONString(); @@ -372,6 +375,7 @@ public class SendEmailUtil { private final static Long UPGRADE_SUCCESS_NOTIFICATION_ID = 118856L; private final static Long UPGRADE_NOTIFICATION_ID_CHINESE = 122898L; private final static Long UPGRADE_SUCCESS_NOTIFICATION_ID_CHINESE = 122899L; + public static void sendUpgradeNotification(Account account, String senderAddress, Integer type) { try { // 实例化一个认证对象 @@ -394,7 +398,7 @@ public class SendEmailUtil { if (type == 1) { subject = "Upcoming System Upgrade for AiDA 3.0"; template.setTemplateID(UPGRADE_NOTIFICATION_ID); - }else { + } else { subject = "即将到来的AiDA 3.0系统升级"; template.setTemplateID(UPGRADE_NOTIFICATION_ID_CHINESE); } @@ -420,7 +424,8 @@ public class SendEmailUtil { } private final static Long GENERATE_EXCEPTION_WARNING_ID = 122589L; - public static void sendGenerateExceptionWarning(String message){ + + public static void sendGenerateExceptionWarning(String message) { try { // 实例化一个认证对象 Credential cred = new Credential(SECRET_ID, SECRET_KEy); @@ -459,7 +464,8 @@ public class SendEmailUtil { private final static Long QUESTIONNAIRE_FEEDBACK_EN_ID = 124151L; private final static Long QUESTIONNAIRE_FEEDBACK_CN_ID = 124156L; - public static void questionnaireRelatedNotify(String userName, String email, String language){ + + public static void questionnaireRelatedNotify(String userName, String email, String language) { try { // 实例化一个认证对象 Credential cred = new Credential(SECRET_ID, SECRET_KEy); @@ -504,7 +510,7 @@ public class SendEmailUtil { private final static Long RENEWAL_NOTIFICATION_FOR_OLD_USER_EN = 124892L; private final static Long RENEWAL_NOTIFICATION_FOR_OLD_USER_CN = 124891L; - public static void notificationForPaidUser(String receiverAddress, int emailType, String country, String userName, String date){ + public static void notificationForPaidUser(String receiverAddress, int emailType, String country, String userName, String date) { try { // 实例化一个认证对象 Credential cred = new Credential(SECRET_ID, SECRET_KEy); @@ -527,7 +533,7 @@ public class SendEmailUtil { subject = "Welcome to AiDA!"; if (country.equals("China")) { template.setTemplateID(NEW_USER_PAYMENT_NOTIFICATION_CN); - }else { + } else { template.setTemplateID(NEW_USER_PAYMENT_NOTIFICATION_EN); } parameter.put("userName", userName); @@ -538,7 +544,7 @@ public class SendEmailUtil { subject = "Account renewal notification"; if (country.equals("China")) { template.setTemplateID(RENEWAL_NOTIFICATION_FOR_OLD_USER_CN); - }else { + } else { template.setTemplateID(RENEWAL_NOTIFICATION_FOR_OLD_USER_EN); } break; @@ -599,8 +605,8 @@ public class SendEmailUtil { private final static Long NEW_USER_REGISTER_NOTIFICATION_EN = 126919L; - public static void notificationForRegisterUser(String receiverAddress){ - try{ + public static void notificationForRegisterUser(String receiverAddress) { + try { // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 @@ -636,8 +642,8 @@ public class SendEmailUtil { private final static Long CHANGE_MAILBOX_CONFIRM_CN = 128278L; private final static Long CHANGE_MAILBOX_CONFIRM_EN = 128277L; - public static void changeMailboxConfirm(String receiverAddress, String language, String name, String link){ - try{ + public static void changeMailboxConfirm(String receiverAddress, String language, String name, String link) { + try { // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 @@ -655,10 +661,10 @@ public class SendEmailUtil { req.setFromEmailAddress(SEND_ADDRESS); req.setDestination(new String[]{receiverAddress}); Template template = new Template(); - if (language.equals("ENGLISH")){ + if (language.equals("ENGLISH")) { req.setSubject("Change the email address bound to the AiDA account"); template.setTemplateID(CHANGE_MAILBOX_CONFIRM_EN); - }else { + } else { req.setSubject("更换AiDA账号绑定的邮箱地址"); template.setTemplateID(CHANGE_MAILBOX_CONFIRM_CN); } @@ -680,12 +686,12 @@ public class SendEmailUtil { private final static Long UPLOAD_TIMEOUT_REMINDER = 128324L; - public static void uploadTimeoutReminder(String userName, String time){ + public static void uploadTimeoutReminder(String userName, String time) { String xp = "xupei3360@163.com"; String shb = "shahaibodd99@gmail.com"; String wxd = "X1627315083@163.com"; String pkc = "kaicpang.pang@connect.polyu.hk"; - try{ + try { // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 @@ -723,6 +729,7 @@ public class SendEmailUtil { private final static Long HALFPRICEPROMOTION_CN_ID = 128582L; private final static Long HALFPRICEPROMOTION_EN_ID = 128583L; + public static void halfPricePromotion(Account account, String senderAddress, int type) { try { // 实例化一个认证对象 @@ -752,7 +759,7 @@ public class SendEmailUtil { if (type == 1) { subject = "AiDA workshop - Win a trip to Hong Kong!"; template.setTemplateID(HALFPRICEPROMOTION_EN_ID); - }else { + } else { subject = "AiDA workshop - 赢取香港之旅"; template.setTemplateID(HALFPRICEPROMOTION_CN_ID); } @@ -784,8 +791,8 @@ public class SendEmailUtil { private final static Long PAYMENT_FAILED_RENEWAL_USER_EN = 131563L; private final static Long PAYMENT_FAILED_RENEWAL_USER_CN = 131564L; - public static void subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress){ - try{ + public static void subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress) { + try { // String merchantEmail = "kimwong@code-create.com.hk"; String merchantEmail = "xupei3360@163.com"; Credential cred = new Credential(SECRET_ID, SECRET_KEy); @@ -819,10 +826,10 @@ public class SendEmailUtil { merchant.setSubject("[Code-Create] Payment Failed : Renewal Order (" + subscriptionEmailParamsDTO.getOrderId() + ")"); templateMerchant.setTemplateID(PAYMENT_FAILED_RENEWAL_MERCHANT_EN); // todo to user - if (language.equals("ENGLISH")){ + if (language.equals("ENGLISH")) { user.setSubject("[Code-Create] Payment Failed : Renewal Order (" + subscriptionEmailParamsDTO.getOrderId() + ")"); templateUser.setTemplateID(PAYMENT_FAILED_RENEWAL_USER_EN); - }else { + } else { user.setSubject("[Code-Create] 自动续费失败 (" + subscriptionEmailParamsDTO.getOrderId() + ")"); templateUser.setTemplateID(PAYMENT_FAILED_RENEWAL_USER_CN); } @@ -830,10 +837,10 @@ public class SendEmailUtil { case "new": merchant.setSubject("[Code-Create] New Order(" + subscriptionEmailParamsDTO.getOrderId() + ")"); templateMerchant.setTemplateID(NEW_MERCHANT_EN); - if (language.equals("ENGLISH")){ + if (language.equals("ENGLISH")) { user.setSubject("[Code-Create] You have successfully subscribed to AiDA"); templateUser.setTemplateID(NEW_USER_EN); - }else { + } else { user.setSubject("[Code-Create] 您已成功订阅AiDA"); templateUser.setTemplateID(NEW_USER_CN); } @@ -841,19 +848,19 @@ public class SendEmailUtil { case "renewal": merchant.setSubject("[Code-Create] New subscription renewal order (" + subscriptionEmailParamsDTO.getOrderId() + ")"); templateMerchant.setTemplateID(RENEWAL_MERCHANT_EN); - if (language.equals("ENGLISH")){ + if (language.equals("ENGLISH")) { user.setSubject("[Code-Create] AiDA Renewal Successful"); templateUser.setTemplateID(RENEWAL_USER_EN); - }else { + } else { user.setSubject("[Code-Create] AiDA续订成功"); templateUser.setTemplateID(RENEWAL_USER_CN); } break; case "reminder": - if (language.equals("ENGLISH")){ + if (language.equals("ENGLISH")) { user.setSubject("[Code-Create] AiDA Subscription Renewal Reminder"); templateUser.setTemplateID(RENEWAL_REMINDER_USER_EN); - }else { + } else { user.setSubject("[Code-Create] AiDA续订提醒"); templateUser.setTemplateID(RENEWAL_REMINDER_USER_CN); } @@ -869,12 +876,12 @@ public class SendEmailUtil { templateMerchant.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO)); merchant.setTemplate(templateMerchant); - if (!type.equals("cancel") && !type.equals("fail_new") ){ + if (!type.equals("cancel") && !type.equals("fail_new")) { // 返回的resp是一个SendEmailResponse的实例,与请求对象对应 SendEmailResponse respUser = client.SendEmail(user); log.info("邮件主题:{},发送结果toUser###{}", user.getSubject(), SendEmailResponse.toJsonString(respUser)); } - if (!type.equals("reminder")){ + if (!type.equals("reminder")) { SendEmailResponse respMerchant = client.SendEmail(merchant); log.info("邮件主题:{},发送结果toMerchant###{}", merchant.getSubject(), SendEmailResponse.toJsonString(respMerchant)); } @@ -883,4 +890,57 @@ public class SendEmailUtil { throw new BusinessException("failed.to.send.mail"); } } + + private final static Long NEW_REGISTRATION = 132123L; + private final static Long AFFILIATE_ACCEPTED = 132124L; + private final static Long AFFILIATE_REFUSED = 132125L; + private final static Long AFFILIATE_MONTHLY_SUMMARY = 132126L; + + public static void affiliateEmailReminder(String receiverAddress, AffiliateEmailParamsDTO paramsDTO, String type) { + try { + Credential cred = new Credential(SECRET_ID, SECRET_KEy); + // 实例化一个http选项,可选的,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("ses.tencentcloudapi.com"); + // 实例化一个client选项,可选的,没有特殊需求可以跳过 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品的client对象,clientProfile是可选的 + SesClient client = new SesClient(cred, "ap-hongkong", clientProfile); + // 实例化一个请求对象,每个接口都会对应一个request对象 + SendEmailRequest req = new SendEmailRequest(); + req.setFromEmailAddress(SEND_ADDRESS); + req.setDestination(new String[]{receiverAddress}); + Template template = new Template(); + switch (type) { + case "new": + req.setSubject("New Affiliate Registration"); + template.setTemplateID(NEW_REGISTRATION); + break; + case "accepted": + req.setSubject("Affiliate Application Accepted"); + template.setTemplateID(AFFILIATE_ACCEPTED); + break; + case "refused": + req.setSubject("Affiliate Application Refused"); + template.setTemplateID(AFFILIATE_REFUSED); + break; + case "summary": + req.setSubject("Your Monthly AffiliateWP Summary for AiDA"); + template.setTemplateID(AFFILIATE_MONTHLY_SUMMARY); + break; + } + + template.setTemplateData(JSON.toJSONString(paramsDTO)); + req.setTemplate(template); + + // 返回的resp是一个SendEmailResponse的实例,与请求对象对应 + SendEmailResponse resp = client.SendEmail(req); + log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp)); + } catch (TencentCloudSDKException e) { + log.info("邮件发送失败###{}", e.toString()); + throw new BusinessException("failed.to.send.mail"); + } + } + } diff --git a/src/main/java/com/ai/da/controller/AffiliateController.java b/src/main/java/com/ai/da/controller/AffiliateController.java new file mode 100644 index 00000000..4e55ce71 --- /dev/null +++ b/src/main/java/com/ai/da/controller/AffiliateController.java @@ -0,0 +1,63 @@ +package com.ai.da.controller; + + +import com.ai.da.common.response.Response; +import com.ai.da.mapper.primary.entity.Affiliate; +import com.ai.da.model.dto.AffiliateQueryDTO; +import com.ai.da.model.vo.AffiliateVO; +import com.ai.da.service.AffiliateService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +@Slf4j +@RestController +@RequestMapping("/api/affiliate") +public class AffiliateController { + + @Resource + private AffiliateService affiliateService; + + @ApiOperation(value = "注册成为affiliate") + @GetMapping("/registration") + public Response completeGuidance(@RequestParam("promotionMethod") String promotionMethod) { + return Response.success(affiliateService.registerAsAnAffiliate(promotionMethod)); + } + + @ApiOperation(value = "获取affiliate列表") + @PostMapping("/list") + public Response> getAffiliateList(@Valid @RequestBody AffiliateQueryDTO affiliateQueryDTO) { + return Response.success(affiliateService.getAffiliateList(affiliateQueryDTO)); + } + + @ApiOperation(value = "获取affiliate个人中心") + @GetMapping("/personalCenter") + public Response personalAffiliateCenter() { + return Response.success(affiliateService.personalAffiliateCenter()); + } + + @ApiOperation(value = "审批affiliate申请") + @GetMapping("/approval") + public Response applicationApproval(@RequestParam("id") Long id, @RequestParam("isApproved")Boolean isApproved) { + return Response.success(affiliateService.applicationApproval(id, isApproved)); + } + + @ApiOperation(value = "审批affiliate申请") + @GetMapping("/testTask") + public Response testTask() { + affiliateService.updateAffiliateInfoWithPayment(); + return Response.success("success "); + } + + @ApiOperation(value = "affiliate链接浏览量增加") + @GetMapping("/viewsIncrease") + public Response viewsGet(@RequestParam("id") Long id) { + return Response.success(affiliateService.affiliateLinkViewsIncrease(id)); + } + + +} diff --git a/src/main/java/com/ai/da/controller/ElementController.java b/src/main/java/com/ai/da/controller/ElementController.java index 40dbcde1..8065c6dc 100644 --- a/src/main/java/com/ai/da/controller/ElementController.java +++ b/src/main/java/com/ai/da/controller/ElementController.java @@ -66,6 +66,8 @@ public class ElementController { return Response.success(); } + /** 该功能已删除 */ + @Deprecated @ApiOperation(value = "生成印花") @PostMapping("/generatePrint") public Response generatePrint(@Valid @RequestBody CollectionGeneratePrintDTO generatePrintDTO) { diff --git a/src/main/java/com/ai/da/controller/StripeController.java b/src/main/java/com/ai/da/controller/StripeController.java index 8becf26b..21c91a86 100644 --- a/src/main/java/com/ai/da/controller/StripeController.java +++ b/src/main/java/com/ai/da/controller/StripeController.java @@ -99,4 +99,10 @@ public class StripeController { return Response.success(stripeService.sendRenewalFailEmail(invoiceId, subscriptionId,orderNo)); } + @ApiOperation("临时 查询指定用户绑定的付款方式") + @GetMapping("/getCustomerPaymentMethod") + public Response getCustomerPaymentMethod(@RequestParam String name, @RequestParam String email) { + return Response.success(stripeService.getCustomerPaymentMethod(name, email)); + } + } diff --git a/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java b/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java new file mode 100644 index 00000000..314a0952 --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java @@ -0,0 +1,7 @@ +package com.ai.da.mapper.primary; + +import com.ai.da.mapper.primary.entity.Affiliate; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +public interface AffiliateMapper extends BaseMapper { +} diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Account.java b/src/main/java/com/ai/da/mapper/primary/entity/Account.java index 285acd90..60fcc9f0 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/Account.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/Account.java @@ -104,4 +104,7 @@ public class Account implements Serializable { * 头像 */ private String avatar; + + private Long invitationCode; + } diff --git a/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java b/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java new file mode 100644 index 00000000..56204d75 --- /dev/null +++ b/src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java @@ -0,0 +1,28 @@ +package com.ai.da.mapper.primary.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@TableName("t_affiliate") +public class Affiliate extends BaseEntity{ + + private Long accountId; + + // Active(活跃) || Inactive(过期) || Pending(待审批) || Refused(拒绝) + private String status; + + private Float totalEarnings = 0.00F; + + private Float monthlyEarnings = 0.00F; + + private Float unpaidEarnings = 0.00F; + + private Integer visits = 0; + + private Boolean approved = false; + + private String link; +} diff --git a/src/main/java/com/ai/da/mapper/primary/entity/OrderInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/OrderInfo.java index e5bc0a98..04a9d894 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/OrderInfo.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/OrderInfo.java @@ -24,4 +24,9 @@ public class OrderInfo extends BaseEntity{ private String note; private String paymentType;//支付方式 + + // 可用于标记用户订单是否首次订阅 + private byte isFirstSubscription = 0; + + private byte isCommissionCalculated = 0; } diff --git a/src/main/java/com/ai/da/model/dto/AffiliateEmailParamsDTO.java b/src/main/java/com/ai/da/model/dto/AffiliateEmailParamsDTO.java new file mode 100644 index 00000000..38c87b72 --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/AffiliateEmailParamsDTO.java @@ -0,0 +1,31 @@ +package com.ai.da.model.dto; + +import lombok.Data; + +@Data +public class AffiliateEmailParamsDTO { + + private String username; + + private String promotionMethod; + + private String totalProgramRevenue; + + private String newApprovedAffiliates; + + private String unpaidEarnings; + + private String paidEarnings; + + public AffiliateEmailParamsDTO() { + } + + public AffiliateEmailParamsDTO(String username) { + this.username = username; + } + + public AffiliateEmailParamsDTO(String username, String promotionMethod) { + this.username = username; + this.promotionMethod = promotionMethod; + } +} diff --git a/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java b/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java new file mode 100644 index 00000000..5498964b --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java @@ -0,0 +1,13 @@ +package com.ai.da.model.dto; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@ApiModel("查询affiliate列表") +public class AffiliateQueryDTO extends TimeQueryBaseDTO{ + + private String status; +} diff --git a/src/main/java/com/ai/da/model/dto/GenerateThroughImageTextDTO.java b/src/main/java/com/ai/da/model/dto/GenerateThroughImageTextDTO.java index 8dbeca50..772cb8cf 100644 --- a/src/main/java/com/ai/da/model/dto/GenerateThroughImageTextDTO.java +++ b/src/main/java/com/ai/da/model/dto/GenerateThroughImageTextDTO.java @@ -12,49 +12,50 @@ import javax.validation.constraints.NotNull; public class GenerateThroughImageTextDTO { @NotNull(message = "userId cannot be empty") @ApiModelProperty("用户id") - Long userId; + private Long userId; @ApiModelProperty("caption | prompt") - String text; + private String text; @ApiModelProperty("图片在t_collection_element表中的id") - Long collectionElementId; + private Long collectionElementId; // todo 后续取消这个字段的传输,由后端自行判断相关参数是否有值 // @NotBlank(message = "you have to choose the generate type") @ApiModelProperty("text image text-image") - String generateType; + private String generateType; @ApiModelProperty("图片来源:update,从library中选择,从toProductImage结果中选择 collection || library || productImage") - String designType; + private String designType; @NotBlank(message = "level1Type cannot be empty!") @ApiModelProperty("Moodboard Printboard Sketchboard MarketingSketch") - String level1Type; + private String level1Type; @ApiModelProperty("Outwear Dress Blouse Skirt Trousers || Logo Slogan Pattern") - String level2Type; + private String level2Type; @ApiModelProperty("性别") - String gender; + private String gender; - @ApiModelProperty("选择的模型名") - String version; + + @ApiModelProperty("选择的模型名 high || fast") + private String version; @NotBlank(message = "timeZone cannot be empty!") @ApiModelProperty("本地时区,比如 'Asia/Tokyo' 东京时间 , 'Asia/Shanghai' 北京时间 由js本地获取") - String timeZone; + private String timeZone; @ApiModelProperty("唯一id,用于保持消息唯一性") - String uniqueId; + private String uniqueId; @NotNull(message = "Please check if the required fields are empty.(isTestUser)") @ApiModelProperty("是否是测试用户") - Boolean isTestUser; + private Boolean isTestUser; @ApiModelProperty("页面上用户设计的slogan所截的图片") - String sloganBase64; + private String sloganBase64; @ApiModelProperty("种子 取值范围 0~500") - String seed; + private String seed; } diff --git a/src/main/java/com/ai/da/model/dto/TimeQueryBaseDTO.java b/src/main/java/com/ai/da/model/dto/TimeQueryBaseDTO.java new file mode 100644 index 00000000..3c10297b --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/TimeQueryBaseDTO.java @@ -0,0 +1,19 @@ +package com.ai.da.model.dto; + +import com.ai.da.model.vo.PageQueryBaseVo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@ApiModel("按时间查询") +public class TimeQueryBaseDTO extends PageQueryBaseVo { + + @ApiModelProperty("按时间区间查询 区间起点") + private String startTime; + + @ApiModelProperty("按时间区间查询 区间终点") + private String endTime; +} diff --git a/src/main/java/com/ai/da/model/vo/AffiliateVO.java b/src/main/java/com/ai/da/model/vo/AffiliateVO.java new file mode 100644 index 00000000..96177e1a --- /dev/null +++ b/src/main/java/com/ai/da/model/vo/AffiliateVO.java @@ -0,0 +1,15 @@ +package com.ai.da.model.vo; + +import com.ai.da.mapper.primary.entity.Affiliate; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AffiliateVO extends Affiliate { + + private Long linkViewCount; + +} diff --git a/src/main/java/com/ai/da/service/AffiliateService.java b/src/main/java/com/ai/da/service/AffiliateService.java new file mode 100644 index 00000000..ed340ae4 --- /dev/null +++ b/src/main/java/com/ai/da/service/AffiliateService.java @@ -0,0 +1,22 @@ +package com.ai.da.service; + +import com.ai.da.mapper.primary.entity.Affiliate; +import com.ai.da.model.dto.AffiliateQueryDTO; +import com.ai.da.model.vo.AffiliateVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +public interface AffiliateService extends IService { + + Boolean registerAsAnAffiliate(String promotionMethod); + + IPage getAffiliateList(AffiliateQueryDTO affiliateQueryDTO); + + AffiliateVO personalAffiliateCenter(); + + Boolean applicationApproval(Long id, Boolean isApproved); + + void updateAffiliateInfoWithPayment(); + + Boolean affiliateLinkViewsIncrease(Long id); +} diff --git a/src/main/java/com/ai/da/service/StripeService.java b/src/main/java/com/ai/da/service/StripeService.java index 268f08d2..f6f60dc3 100644 --- a/src/main/java/com/ai/da/service/StripeService.java +++ b/src/main/java/com/ai/da/service/StripeService.java @@ -19,6 +19,8 @@ public interface StripeService { List getSubscriptionIds(String name, String userEmail) throws StripeException; + Map getPaymentMethodByInvoiceId(String invoiceId); + void cancelSubscription(String orderNo); void cancelSubscriptionTemp(String subscriptionId); @@ -38,4 +40,6 @@ public interface StripeService { String changeCustomerPayment(String name, String email); boolean sendRenewalFailEmail(String invoiceId, String subscriptionId, String orderNo); + + String getCustomerPaymentMethod(String name, String email); } diff --git a/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java new file mode 100644 index 00000000..47f2a0b6 --- /dev/null +++ b/src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java @@ -0,0 +1,189 @@ +package com.ai.da.service.impl; + +import com.ai.da.common.config.exception.BusinessException; +import com.ai.da.common.constant.CommonConstant; +import com.ai.da.common.context.UserContext; +import com.ai.da.common.response.ResultEnum; +import com.ai.da.common.utils.CopyUtil; +import com.ai.da.common.utils.ObjectUtils; +import com.ai.da.common.utils.RedisUtil; +import com.ai.da.common.utils.SendEmailUtil; +import com.ai.da.mapper.primary.AffiliateMapper; +import com.ai.da.mapper.primary.entity.*; +import com.ai.da.model.dto.AffiliateEmailParamsDTO; +import com.ai.da.model.dto.AffiliateQueryDTO; +import com.ai.da.model.vo.AffiliateVO; +import com.ai.da.model.vo.AuthPrincipalVo; +import com.ai.da.service.AccountService; +import com.ai.da.service.AffiliateService; +import com.ai.da.service.OrderInfoService; +import com.ai.da.service.PaymentInfoService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import io.netty.util.internal.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +@Service +@Slf4j +public class AffiliateServiceImpl extends ServiceImpl implements AffiliateService { + + @Resource + private OrderInfoService orderInfoService; + + @Resource + private AccountService accountService; + + @Resource + private PaymentInfoService paymentInfoService; + + @Resource + private RedisUtil redisUtil; + + // 推广者注册 + public Boolean registerAsAnAffiliate(String promotionMethod){ + AuthPrincipalVo userHolder = UserContext.getUserHolder(); + // 判断该用户是否已注册 + QueryWrapper qw = new QueryWrapper<>(); + qw.eq("account_id", userHolder.getId()); + Affiliate affiliate = baseMapper.selectOne(qw); + if (Objects.isNull(affiliate)){ + affiliate = new Affiliate(); + affiliate.setAccountId(userHolder.getId()); + affiliate.setStatus("Pending"); + affiliate.setCreateTime(LocalDateTime.now()); + baseMapper.insert(affiliate); + // 邮件通知审批者 +// String email = "kimwong@code-create.com.hk"; + String email = "xupei3360@163.com"; + SendEmailUtil.affiliateEmailReminder(email, new AffiliateEmailParamsDTO(userHolder.getUsername(), promotionMethod), "new"); + }else { + throw new BusinessException("You have registered an Affiliate", ResultEnum.PROMPT.getCode()); + } + return true; + } + + public IPage getAffiliateList(AffiliateQueryDTO affiliateQueryDTO){ + QueryWrapper qw = new QueryWrapper<>(); + qw.eq(affiliateQueryDTO.getStatus() != null, "status", affiliateQueryDTO.getStatus()); + qw.gt(affiliateQueryDTO.getStartTime() != null, "create_time", affiliateQueryDTO.getStartTime()); + qw.lt(affiliateQueryDTO.getEndTime() != null, "create_time", affiliateQueryDTO.getEndTime()); + return baseMapper.selectPage(new Page<>(affiliateQueryDTO.getPage(), affiliateQueryDTO.getSize()), qw); + } + + public AffiliateVO personalAffiliateCenter(){ + QueryWrapper qw = new QueryWrapper<>(); + Long accountId = UserContext.getUserHolder().getId(); + qw.eq("account_id", accountId); + Affiliate affiliate = baseMapper.selectOne(qw); + AffiliateVO affiliateVO = CopyUtil.copyObject(affiliate, AffiliateVO.class); + affiliateVO.setLinkViewCount(getAffiliateLinkViewCount(accountId)); + return affiliateVO; + } + + // 审批申请 + public Boolean applicationApproval(Long id, Boolean isApproved){ + Affiliate affiliate = baseMapper.selectById(id); + + // 1、更新db状态 + if (isApproved){ + // 更新状态 + affiliate.setStatus("Active"); + affiliate.setApproved(true); + affiliate.setLink(CommonConstant.AFFILIATE_LINK + affiliate.getId()); + } else { + affiliate.setStatus("Inactive"); + affiliate.setApproved(false); + } + affiliate.setUpdateTime(LocalDateTime.now()); + + // 2、将批准结果邮件通知用户 + Account account = accountService.getById(affiliate.getAccountId()); + String userEmail = account.getUserEmail(); + String userName = account.getUserName(); + if (isApproved){ + SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "accepted"); + }else { + SendEmailUtil.affiliateEmailReminder(userEmail, new AffiliateEmailParamsDTO(userName), "refused"); + } + return true; + } + + // 定时计算佣金 + public void updateAffiliateInfoWithPayment(){ + // id存redis + String lastTime = redisUtil.getFromString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME); + String currentTime = LocalDateTime.now().toString(); + // 1、查上次更新之后有无新订单 + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (!StringUtil.isNullOrEmpty(lastTime)){ + queryWrapper.gt("create_time", lastTime); + } + queryWrapper.eq("type","new").eq("trade_state", "paid"); + + List paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper); + if (!paymentInfos.isEmpty()){ + paymentInfos.forEach(paymentInfo -> { + // 2、根据order_no查付款用户id + OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(paymentInfo.getOrderNo()); + Long accountId = orderInfo.getAccountId(); + // 3、查该用户之前是否有初次订阅的订单 + QueryWrapper qwOrderInfo = new QueryWrapper<>(); + qwOrderInfo.eq("account_id", accountId).eq("is_first_subscription", 1); + List orderInfos = orderInfoService.getBaseMapper().selectList(qwOrderInfo); + // 该用户首次订阅(非首次订阅,不分配佣金) + if (orderInfos.isEmpty()){ + // 查询是否绑定affiliateId + Account account = accountService.getById(accountId); + if (!Objects.isNull(account.getInvitationCode())){ + // 3、若有, 直接更新affiliate的所得 + Affiliate affiliate = baseMapper.selectById(account.getInvitationCode()); + Float payerTotal = paymentInfo.getPayerTotal(); + if (payerTotal > 0){ + // 分配新用户首次订阅所付费用的25%作为佣金 + BigDecimal commission = BigDecimal.valueOf(payerTotal).multiply(new BigDecimal("0.25")); + BigDecimal monthlyEarning = BigDecimal.valueOf(affiliate.getMonthlyEarnings()).add(commission); + BigDecimal unpaidEarnings = BigDecimal.valueOf(affiliate.getUnpaidEarnings()).add(commission); + int visits = affiliate.getVisits() + 1; + affiliate.setMonthlyEarnings(monthlyEarning.floatValue()); + affiliate.setUnpaidEarnings(unpaidEarnings.floatValue()); + affiliate.setVisits(visits); + affiliate.setUpdateTime(LocalDateTime.now()); + baseMapper.updateById(affiliate); + + orderInfo.setIsCommissionCalculated((byte)1); + } + } + orderInfo.setIsFirstSubscription((byte)1); + orderInfo.setUpdateTime(LocalDateTime.now()); + orderInfoService.updateById(orderInfo); + } + }); + } + redisUtil.addToString(RedisUtil.PAYMENT_INFO_LAST_SCAN_TIME, currentTime); + } + + public Boolean affiliateLinkViewsIncrease(Long id) { + redisUtil.increaseAffiliateLinkViewCount(id); + return Boolean.TRUE; + } + + private Long getAffiliateLinkViewCount(Long accountId) { + return redisUtil.getAffiliateLinkViewCount(accountId); + } + + // todo 每个月给kim发一封邮件统计本月的affiliate等的收入 + public void commissionCalculation(){ + + } + + +} diff --git a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java index a6bcedc6..e0f86569 100644 --- a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java @@ -216,6 +216,8 @@ public class CollectionElementServiceImpl extends ServiceImpl implements PaymentInfoService { - private final StripeServiceImpl stripeServiceImpl; - - public PaymentInfoServiceImpl(StripeServiceImpl stripeServiceImpl) { - this.stripeServiceImpl = stripeServiceImpl; - } + @Resource + private StripeService stripeService; /** * 记录支付日志:微信支付 @@ -220,7 +218,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl paymentMethod = stripeServiceImpl.getPaymentMethodByInvoiceId(invoiceId); + Map paymentMethod = stripeService.getPaymentMethodByInvoiceId(invoiceId); paymentInfo = new PaymentInfo(); paymentInfo.setOrderNo(orderNo); 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 3e8238bc..35b44944 100644 --- a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java @@ -553,7 +553,8 @@ public class StripeServiceImpl implements StripeService { Subscription cancel = subscription.cancel(); cancel.getStatus(); } catch (StripeException e) { - throw new RuntimeException(e); + log.error(e.getMessage()); +// throw new RuntimeException(e); } } @@ -988,7 +989,8 @@ public class StripeServiceImpl implements StripeService { try { OrderInfo orderInfo = orderInfoService.createOrderByProductId(1, PayTypeEnum.STRIPE.getType(), ProductEnum.DailySubscription); - String paymentMethodCode = "pm_card_mastercard"; + String customerId = getCustomer(name, email); +/* String paymentMethodCode = "pm_card_mastercard"; PaymentMethod paymentMethod = PaymentMethod.retrieve(paymentMethodCode); String customerId = getCustomer(name, email); @@ -997,14 +999,14 @@ public class StripeServiceImpl implements StripeService { PaymentMethodAttachParams attachParams = PaymentMethodAttachParams.builder() .setCustomer(customerId) .build(); - paymentMethod.attach(attachParams); + paymentMethod.attach(attachParams);*/ // 设置默认付款方式 Customer updatedCustomer = Customer.retrieve(customerId); CustomerUpdateParams params = CustomerUpdateParams.builder() .setInvoiceSettings( CustomerUpdateParams.InvoiceSettings.builder() - .setDefaultPaymentMethod(paymentMethod.getId()) +// .setDefaultPaymentMethod(paymentMethod.getId()) .build() ) .build(); @@ -1064,4 +1066,22 @@ public class StripeServiceImpl implements StripeService { } } + public String getCustomerPaymentMethod(String name, String email){ + Stripe.apiKey = privateKey; + try { + String customerId = getCustomer(name, email); + Customer customer = Customer.retrieve(customerId); + PaymentMethodCollection paymentMethodCollection = customer.listPaymentMethods(); + List data = paymentMethodCollection.getData(); + + // todo 方向: 向用户添加了多种付款方式,更改默认的付款方式后,默认付款方式付款失败后是否自动使用其他付款方式付款? + // 如果是的,则需要删除能成功的付款方式,保留唯一失败的付款方式进行续订付款失败测试 + } catch (StripeException e) { + throw new RuntimeException(e); + } + return null; + } + + + }