From fd10d4dbc448f1c53b7721b5b23416f260cc5e21 Mon Sep 17 00:00:00 2001
From: shahaibo <1023316923@qq.com>
Date: Wed, 13 Nov 2024 15:44:48 +0800
Subject: [PATCH 01/91] =?UTF-8?q?TASK:=E8=B0=B7=E6=AD=8C=E7=99=BB=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 22 +++
.../ai/da/controller/AccountController.java | 35 ++++
.../da/controller/ThirdPartyController.java | 8 +
.../ai/da/mapper/primary/entity/Account.java | 14 ++
.../com/ai/da/model/dto/AccountLoginDTO.java | 2 +
.../com/ai/da/model/dto/AddSubAccountDTO.java | 12 ++
.../ai/da/model/dto/SubAccountPageDTO.java | 9 +
.../com/ai/da/service/AccountService.java | 16 ++
.../da/service/impl/AccountServiceImpl.java | 187 ++++++++++++++++++
9 files changed, 305 insertions(+)
create mode 100644 src/main/java/com/ai/da/model/dto/AddSubAccountDTO.java
create mode 100644 src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java
diff --git a/pom.xml b/pom.xml
index e20e83e1..ff9df865 100644
--- a/pom.xml
+++ b/pom.xml
@@ -288,6 +288,28 @@
spring-boot-starter-websocket
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 1.8.0
+
+
+
+ com.google.api-client
+ google-api-client
+ 1.32.1
+
+
+ com.google.oauth-client
+ google-oauth-client
+ 1.32.1
+
+
+ com.google.http-client
+ google-http-client-jackson2
+ 1.41.5
+
+
diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java
index c73fcdaa..0332154a 100644
--- a/src/main/java/com/ai/da/controller/AccountController.java
+++ b/src/main/java/com/ai/da/controller/AccountController.java
@@ -3,6 +3,7 @@ package com.ai.da.controller;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.Response;
+import com.ai.da.mapper.primary.entity.Account;
import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
@@ -261,5 +262,39 @@ public class AccountController {
return Response.success(true);
}
+ @PostMapping("enterpriseLogin")
+ @ApiOperation(value = "企业登录")
+ public Response enterpriseLogin(@Valid @RequestBody AccountLoginDTO accountDTO) {
+ return Response.success(accountService.enterpriseLogin(accountDTO));
+ }
+ @PostMapping("schoolLogin")
+ @ApiOperation(value = "学校登录")
+ public Response schoolLogin(@Valid @RequestBody AccountLoginDTO accountDTO) {
+ return Response.success(accountService.schoolLogin(accountDTO));
+ }
+
+ @PostMapping("addOrUpdateSubAccount")
+ @ApiOperation(value = "子账号新增")
+ public Response addSubAccount(@Valid @RequestBody AddSubAccountDTO addSubAccountDTO) {
+ return Response.success(accountService.addSubAccount(addSubAccountDTO));
+ }
+
+ @PostMapping("deleteSubAccount")
+ @ApiOperation(value = "子账号删除")
+ public Response deleteSubAccount(@Valid @RequestBody AddSubAccountDTO addSubAccountDTO) {
+ return Response.success(accountService.deleteSubAccount(addSubAccountDTO));
+ }
+
+ @PostMapping("subAccountList")
+ @ApiOperation(value = "子账号查询")
+ public Response> subAccountList(@Valid @RequestBody SubAccountPageDTO subAccountPageDTO) {
+ return Response.success(accountService.subAccountList(subAccountPageDTO));
+ }
+
+ @GetMapping("accountDetail")
+ @ApiOperation(value = "账号详情")
+ public Response accountDetail(@RequestParam("id") Long id) {
+ return Response.success(accountService.accountDetail(id));
+ }
}
diff --git a/src/main/java/com/ai/da/controller/ThirdPartyController.java b/src/main/java/com/ai/da/controller/ThirdPartyController.java
index e077fa8c..e0047d62 100644
--- a/src/main/java/com/ai/da/controller/ThirdPartyController.java
+++ b/src/main/java/com/ai/da/controller/ThirdPartyController.java
@@ -1,6 +1,7 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
+import com.ai.da.mapper.primary.entity.GoogleUser;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
import com.ai.da.service.AccountService;
@@ -121,4 +122,11 @@ public class ThirdPartyController {
public Response googleCallback(@RequestParam("code") String code, HttpSession session) {
return Response.success(accountService.googleCallback(code, session));
}
+ @CrossOrigin
+ @GetMapping("/parseGoogleCredential")
+ public Response parseGoogleCredential(@RequestParam("credential") String credential) {
+ return Response.success(accountService.parseGoogleCredential(credential));
+ }
+
+
}
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..3629eedf 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
@@ -97,6 +97,10 @@ public class Account implements Serializable {
* 2 : 月付用户
* 3 : 试用用户
* 4 : 参加活动获取30天有效期和6000个积分的用户
+ * 5 : 企业管理员账号
+ * 6 : 企业子账号
+ * 7 : 学校管理员
+ * 8 : 学校子账号
*/
private Integer systemUser;
@@ -104,4 +108,14 @@ public class Account implements Serializable {
* 头像
*/
private String avatar;
+
+ private String organizationName;
+
+ private Long parentId;
+
+ private Integer isAdmin;
+
+ private BigDecimal shareCredits;
+
+ private Integer subAccountNum;
}
diff --git a/src/main/java/com/ai/da/model/dto/AccountLoginDTO.java b/src/main/java/com/ai/da/model/dto/AccountLoginDTO.java
index 2c1483c9..2f536ffb 100644
--- a/src/main/java/com/ai/da/model/dto/AccountLoginDTO.java
+++ b/src/main/java/com/ai/da/model/dto/AccountLoginDTO.java
@@ -32,4 +32,6 @@ public class AccountLoginDTO {
@ApiModelProperty("邮箱验证码")
private String emailVerifyCode;
+ private String organizationName;
+
}
diff --git a/src/main/java/com/ai/da/model/dto/AddSubAccountDTO.java b/src/main/java/com/ai/da/model/dto/AddSubAccountDTO.java
new file mode 100644
index 00000000..2f894789
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/AddSubAccountDTO.java
@@ -0,0 +1,12 @@
+package com.ai.da.model.dto;
+
+import com.ai.da.mapper.primary.entity.Account;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class AddSubAccountDTO extends Account {
+
+ private List deleteIdList;
+}
diff --git a/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java b/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java
new file mode 100644
index 00000000..f5dcc995
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java
@@ -0,0 +1,9 @@
+package com.ai.da.model.dto;
+
+import com.ai.da.model.vo.PageQueryBaseVo;
+import lombok.Data;
+
+@Data
+public class SubAccountPageDTO extends PageQueryBaseVo {
+ private String userName;
+}
diff --git a/src/main/java/com/ai/da/service/AccountService.java b/src/main/java/com/ai/da/service/AccountService.java
index bd9c99b4..2d86fdf2 100644
--- a/src/main/java/com/ai/da/service/AccountService.java
+++ b/src/main/java/com/ai/da/service/AccountService.java
@@ -1,6 +1,8 @@
package com.ai.da.service;
+import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.Account;
+import com.ai.da.mapper.primary.entity.GoogleUser;
import com.ai.da.mapper.primary.entity.TrialOrder;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
@@ -186,4 +188,18 @@ public interface AccountService extends IService {
String googleCallback(String code, HttpSession session);
List getPaidCustomerEmail();
+
+ AccountLoginVO enterpriseLogin(AccountLoginDTO accountDTO);
+
+ AccountLoginVO schoolLogin(AccountLoginDTO accountDTO);
+
+ Boolean addSubAccount(AddSubAccountDTO addSubAccountDTO);
+
+ Boolean deleteSubAccount(AddSubAccountDTO addSubAccountDTO);
+
+ PageBaseResponse subAccountList(SubAccountPageDTO subAccountPageDTO);
+
+ Account accountDetail(Long id);
+
+ GoogleUser parseGoogleCredential(String credential);
}
diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
index bead403e..03145804 100644
--- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
@@ -7,6 +7,7 @@ import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.AuthenticationOperationTypeEnum;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.LoginTypeEnum;
+import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.*;
@@ -25,6 +26,13 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
+import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.jackson2.JacksonFactory;
+import com.google.auth.oauth2.IdToken;
+import com.google.auth.oauth2.TokenVerifier;
+import com.google.common.base.Function;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.netty.util.internal.StringUtil;
@@ -1945,4 +1953,183 @@ public class AccountServiceImpl extends ServiceImpl impl
// return userRepository.save(newUser);
// }
+
+ @Override
+ public AccountLoginVO enterpriseLogin(AccountLoginDTO accountDTO) {
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.lambda().eq(Account::getUserName, accountDTO.getUserName());
+ qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName());
+ List accounts = accountMapper.selectList(qw);
+ if (CollectionUtil.isEmpty(accounts)) {
+ throw new BusinessException("Username or enterprise name incorrect.");
+ }
+ qw.lambda().eq(Account::getUserPassword, accountDTO.getPassword());
+ accounts = accountMapper.selectList(qw);
+ if (CollectionUtil.isEmpty(accounts)) {
+ throw new BusinessException("Password error.");
+ }
+ Account account = accounts.get(0);
+ AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
+ response.setEmail(account.getUserEmail());
+ String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
+ if (StringUtils.isNotBlank(token)) {
+ //用户已登入
+ response.setToken(token);
+ } else {
+ response.setToken(createAccountToken(account));
+ }
+ response.setUserId(account.getId());
+ response.setSystemUser(account.getSystemUser());
+ // 设置头像
+ String avatar;
+ if (StringUtil.isNullOrEmpty(account.getAvatar())){
+ avatar = CommonConstant.DEFAULT_AVATAR;
+ }else {
+ avatar = account.getAvatar();
+ }
+ response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
+ response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
+ response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ //判断是否常用ip 不是则发邮件提示
+// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
+ return response;
+ }
+
+ @Override
+ public AccountLoginVO schoolLogin(AccountLoginDTO accountDTO) {
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.lambda().eq(Account::getUserName, accountDTO.getUserName());
+ qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName());
+ List accounts = accountMapper.selectList(qw);
+ if (CollectionUtil.isEmpty(accounts)) {
+ throw new BusinessException("Username or school name incorrect.");
+ }
+ qw.lambda().eq(Account::getUserPassword, accountDTO.getPassword());
+ accounts = accountMapper.selectList(qw);
+ if (CollectionUtil.isEmpty(accounts)) {
+ throw new BusinessException("Password error.");
+ }
+ Account account = accounts.get(0);
+ AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
+ response.setEmail(account.getUserEmail());
+ String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
+ if (StringUtils.isNotBlank(token)) {
+ //用户已登入
+ response.setToken(token);
+ } else {
+ response.setToken(createAccountToken(account));
+ }
+ response.setUserId(account.getId());
+ response.setSystemUser(account.getSystemUser());
+ // 设置头像
+ String avatar;
+ if (StringUtil.isNullOrEmpty(account.getAvatar())){
+ avatar = CommonConstant.DEFAULT_AVATAR;
+ }else {
+ avatar = account.getAvatar();
+ }
+ response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
+ response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
+ response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ //判断是否常用ip 不是则发邮件提示
+// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
+ return response;
+ }
+
+ @Override
+ public Boolean addSubAccount(AddSubAccountDTO addSubAccountDTO) {
+ if (null == addSubAccountDTO.getId()) {
+ AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
+ Account account = accountMapper.selectById(authPrincipalVo.getId());
+
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
+ List accounts = accountMapper.selectList(qw);
+ if (accounts.size() >= account.getSubAccountNum()) {
+ throw new BusinessException("The maximum number of sub accounts that can be created has been reached.");
+ }
+
+ qw.lambda().eq(Account::getUserName, addSubAccountDTO.getUserName());
+ accounts = accountMapper.selectList(qw);
+ if (CollectionUtil.isNotEmpty(accounts)) {
+ throw new BusinessException("The enterprise already has an account with the same username.");
+ }
+
+ Account subAccount = CopyUtil.copyObject(addSubAccountDTO, Account.class);
+ subAccount.setSystemUser(6);
+ subAccount.setValidStartTime(account.getValidStartTime());
+ subAccount.setValidEndTime(account.getValidEndTime());
+ subAccount.setLanguage(Language.ENGLISH.name());
+ subAccount.setCreateDate(new Date());
+ subAccount.setIsTrial(0);
+ subAccount.setIsBeginner(1);
+ subAccount.setParentId(account.getParentId());
+ accountMapper.insert(subAccount);
+ }else {
+ Account subAccount = CopyUtil.copyObject(addSubAccountDTO, Account.class);
+ accountMapper.updateById(subAccount);
+ }
+
+
+ return Boolean.TRUE;
+ }
+
+ @Override
+ public Boolean deleteSubAccount(AddSubAccountDTO addSubAccountDTO) {
+ accountMapper.deleteBatchIds(addSubAccountDTO.getDeleteIdList());
+ return Boolean.TRUE;
+ }
+
+ @Override
+ public PageBaseResponse subAccountList(SubAccountPageDTO subAccountPageDTO) {
+ AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
+ Account account = accountMapper.selectById(authPrincipalVo);
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.lambda().ne(Account::getId, account.getId());
+ qw.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
+ // 执行分页查询
+ IPage page = accountMapper.selectPage(new Page<>(subAccountPageDTO.getPage(), subAccountPageDTO.getSize()), qw);
+
+ return PageBaseResponse.success(page);
+ }
+
+ @Override
+ public Account accountDetail(Long id) {
+ return accountMapper.selectById(id);
+ }
+
+ @Override
+ public GoogleUser parseGoogleCredential(String credential) {
+ try {
+ // 配置 Google ID Token 验证器
+ GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
+ new NetHttpTransport(),
+ JacksonFactory.getDefaultInstance())
+ .setAudience(Collections.singletonList(CLIENT_ID))
+ .build();
+
+ // 验证并解析 ID Token
+ GoogleIdToken idToken = verifier.verify(credential);
+
+ if (idToken != null) {
+ GoogleIdToken.Payload payload = idToken.getPayload();
+
+ // 提取用户信息
+ String userId = payload.getSubject();
+ String email = payload.getEmail();
+ String name = (String) payload.get("name");
+ log.info(userId);
+ log.info(email);
+ log.info(name);
+
+ return new GoogleUser();
+ } else {
+ throw new IllegalArgumentException("Invalid ID token.");
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Failed to verify ID token: " + e.getMessage());
+ }
+ }
}
From 9f064609e715a00d3b63f05cf41f1a1658309216 Mon Sep 17 00:00:00 2001
From: shahaibo <1023316923@qq.com>
Date: Wed, 13 Nov 2024 15:45:40 +0800
Subject: [PATCH 02/91] =?UTF-8?q?TASK:=E8=B0=B7=E6=AD=8C=E7=99=BB=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/ai/da/common/security/filter/AuthenticationFilter.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
index a70bc51b..05117ec4 100644
--- a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
+++ b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
@@ -51,7 +51,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
- "/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback"
+ "/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential"
);
@Override
From 8d27b5b51ebb1172cefd29293fe115675eb1b09a Mon Sep 17 00:00:00 2001
From: xupei
Date: Mon, 18 Nov 2024 16:20:25 +0800
Subject: [PATCH 03/91] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=20--?=
=?UTF-8?q?=20=E4=BA=A7=E5=93=81=E8=AE=A2=E9=98=85=20=E5=B9=B4=E5=BA=A6/?=
=?UTF-8?q?=E6=9C=88=E5=BA=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ai/da/common/constant/CommonConstant.java | 4 +
.../com/ai/da/common/enums/ProductEnum.java | 25 +
.../com/ai/da/common/task/AliPayTask.java | 42 --
.../com/ai/da/common/task/GenerateTask.java | 21 -
.../com/ai/da/common/task/PaymentTask.java | 96 +++
.../com/ai/da/common/task/PaypalTask.java | 42 --
.../com/ai/da/common/task/StripeTask.java | 40 --
.../com/ai/da/common/utils/SendEmailUtil.java | 92 +++
.../ai/da/controller/StripeController.java | 32 +-
.../primary/SubscriptionInfoMapper.java | 8 +
.../da/mapper/primary/entity/PaymentInfo.java | 10 +-
.../primary/entity/SubscriptionInfo.java | 43 ++
.../ai/da/model/dto/ProductPurchaseDTO.java | 32 +
.../model/dto/SubscriptionEmailParamsDTO.java | 47 ++
.../com/ai/da/service/OrderInfoService.java | 6 +
.../com/ai/da/service/PaymentInfoService.java | 7 +-
.../java/com/ai/da/service/StripeService.java | 24 +-
.../da/service/impl/OrderInfoServiceImpl.java | 74 +-
.../service/impl/PaymentInfoServiceImpl.java | 61 +-
.../ai/da/service/impl/StripeServiceImpl.java | 633 ++++++++++++++----
src/main/resources/payment.properties | 8 +-
21 files changed, 1029 insertions(+), 318 deletions(-)
create mode 100644 src/main/java/com/ai/da/common/enums/ProductEnum.java
delete mode 100644 src/main/java/com/ai/da/common/task/AliPayTask.java
delete mode 100644 src/main/java/com/ai/da/common/task/GenerateTask.java
create mode 100644 src/main/java/com/ai/da/common/task/PaymentTask.java
delete mode 100644 src/main/java/com/ai/da/common/task/PaypalTask.java
delete mode 100644 src/main/java/com/ai/da/common/task/StripeTask.java
create mode 100644 src/main/java/com/ai/da/mapper/primary/SubscriptionInfoMapper.java
create mode 100644 src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
create mode 100644 src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
create mode 100644 src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
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 71f6c86b..e495ee26 100644
--- a/src/main/java/com/ai/da/common/constant/CommonConstant.java
+++ b/src/main/java/com/ai/da/common/constant/CommonConstant.java
@@ -75,5 +75,9 @@ public class CommonConstant {
public static final String PORTFOLIO_DELETED_CN = "作品已删除";
+ public static final String TIME_FORMAT_MMM_dd_yyyy_EEEE = "MMM. dd, yyyy, EEEE";
+
+ public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy";
+
}
diff --git a/src/main/java/com/ai/da/common/enums/ProductEnum.java b/src/main/java/com/ai/da/common/enums/ProductEnum.java
new file mode 100644
index 00000000..11237ffe
--- /dev/null
+++ b/src/main/java/com/ai/da/common/enums/ProductEnum.java
@@ -0,0 +1,25 @@
+package com.ai.da.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum ProductEnum {
+ // 积分购买
+ CreditsProduct("AiDA credits purchase", 6L),
+ // 年度订阅
+ AnnualSubscription("AiDA Annual Subscription", 5000L),
+ // 月度订阅
+ MonthlySubscription("AiDA Monthly Subscription", 500L),
+ // 测试
+ DailySubscription("AiDA Daily Subscription", 5L),
+ ;
+
+ /**
+ * 类型
+ */
+ private final String name;
+
+ private final Long price;
+}
diff --git a/src/main/java/com/ai/da/common/task/AliPayTask.java b/src/main/java/com/ai/da/common/task/AliPayTask.java
deleted file mode 100644
index 670a417c..00000000
--- a/src/main/java/com/ai/da/common/task/AliPayTask.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.ai.da.common.task;
-
-import com.ai.da.mapper.primary.entity.OrderInfo;
-import com.ai.da.common.enums.PayTypeEnum;
-import com.ai.da.service.AliPayService;
-import com.ai.da.service.OrderInfoService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Resource;
-import java.util.List;
-
-@Slf4j
-@Component
-public class AliPayTask {
-
- @Resource
- private OrderInfoService orderInfoService;
-
- @Resource
- private AliPayService aliPayService;
-
- /**
- * 从第0秒开始每隔30秒执行1次,查询创建超过5分钟,并且未支付的订单
- */
-// @Scheduled(cron = "0/30 * * * * ?")
- public void orderConfirm(){
-
-// log.info("Alipay orderConfirm 被执行......");
-
- List orderInfoList = orderInfoService.getNoPayOrderByDuration(5, PayTypeEnum.ALIPAY.getType());
-
- for (OrderInfo orderInfo : orderInfoList) {
- String orderNo = orderInfo.getOrderNo();
- log.warn("超时订单 ===> {}", orderNo);
-
- //核实订单状态:调用支付宝查单接口
- aliPayService.checkOrderStatus(orderNo);
- }
- }
-}
diff --git a/src/main/java/com/ai/da/common/task/GenerateTask.java b/src/main/java/com/ai/da/common/task/GenerateTask.java
deleted file mode 100644
index 038976d0..00000000
--- a/src/main/java/com/ai/da/common/task/GenerateTask.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.ai.da.common.task;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-@Component
-@Slf4j
-public class GenerateTask {
-
-// @Scheduled(cron = "0 0 */1 * * ?")
- public void generateScheduled(){
- log.info("测试定时器:generate");
-
- try{
-
- }catch(Exception e){
-
- }
- }
-}
diff --git a/src/main/java/com/ai/da/common/task/PaymentTask.java b/src/main/java/com/ai/da/common/task/PaymentTask.java
new file mode 100644
index 00000000..656e3a23
--- /dev/null
+++ b/src/main/java/com/ai/da/common/task/PaymentTask.java
@@ -0,0 +1,96 @@
+package com.ai.da.common.task;
+
+import com.ai.da.common.enums.PayTypeEnum;
+import com.ai.da.mapper.primary.entity.OrderInfo;
+import com.ai.da.service.AliPayService;
+import com.ai.da.service.OrderInfoService;
+import com.ai.da.service.PayPalCheckoutService;
+import com.ai.da.service.StripeService;
+import com.paypal.http.exceptions.SerializeException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Slf4j
+@Component
+public class PaymentTask {
+
+ @Resource
+ private OrderInfoService orderInfoService;
+
+ @Resource
+ private StripeService stripeService;
+
+// @Scheduled(cron = "0/30 * * * * ?")
+ public void orderConfirmForStripe() throws SerializeException {
+
+ // 查看超过30分钟以上仍未支付的订单 置为超时订单
+ List orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.STRIPE.getType());
+
+ for (OrderInfo orderInfo : orderInfoList) {
+ String orderNo = orderInfo.getOrderNo();
+ log.warn("超时订单 ===> {}", orderNo);
+
+ //核实订单状态:调用支付宝查单接口
+ stripeService.checkOrderStatus(orderNo);
+
+ }
+ }
+
+ @Resource
+ private PayPalCheckoutService payPalCheckoutService;
+
+ // @Scheduled(cron = "0/30 * * * * ?")
+ public void orderConfirmForPaypal() throws SerializeException {
+
+// log.info("PayPal orderConfirm 被执行......");
+
+ List orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.PAYPAL.getType());
+
+ for (OrderInfo orderInfo : orderInfoList) {
+ String orderNo = orderInfo.getOrderNo();
+ log.warn("超时订单 ===> {}", orderNo);
+
+ //核实订单状态:调用支付宝查单接口
+ payPalCheckoutService.checkOrderStatus(orderNo);
+
+ }
+ }
+
+ @Resource
+ private AliPayService aliPayService;
+
+ /**
+ * 从第0秒开始每隔30秒执行1次,查询创建超过5分钟,并且未支付的订单
+ */
+// @Scheduled(cron = "0/30 * * * * ?")
+ public void orderConfirmForAlipay(){
+/*
+ log.info("Alipay orderConfirm 被执行......");
+
+ List orderInfoList = orderInfoService.getNoPayOrderByDuration(5, PayTypeEnum.ALIPAY.getType());
+
+ for (OrderInfo orderInfo : orderInfoList) {
+ String orderNo = orderInfo.getOrderNo();
+ log.warn("超时订单 ===> {}", orderNo);
+
+ //核实订单状态:调用支付宝查单接口
+ aliPayService.checkOrderStatus(orderNo);
+ }*/
+ }
+
+ // 提前7天向用户发送提醒邮件,每天早上8点执行
+ @Scheduled(cron = "0 0 8 * * ?")
+ public void subscriptionReminder(){
+ stripeService.subscriptionReminder();
+ }
+
+ // 每天凌晨检查subscription中有哪些已过期,更新状态
+ @Scheduled(cron = "0 0 0 * * ?")
+ public void checkSubscriptionExpiration(){
+ stripeService.checkSubscriptionExpiration();
+ }
+}
diff --git a/src/main/java/com/ai/da/common/task/PaypalTask.java b/src/main/java/com/ai/da/common/task/PaypalTask.java
deleted file mode 100644
index fa069971..00000000
--- a/src/main/java/com/ai/da/common/task/PaypalTask.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.ai.da.common.task;
-
-import com.ai.da.common.enums.PayTypeEnum;
-import com.ai.da.mapper.primary.entity.OrderInfo;
-import com.ai.da.service.OrderInfoService;
-import com.ai.da.service.PayPalCheckoutService;
-import com.paypal.http.exceptions.SerializeException;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.List;
-
-@Slf4j
-@Component
-public class PaypalTask {
-
- @Resource
- private OrderInfoService orderInfoService;
-
- @Resource
- private PayPalCheckoutService payPalCheckoutService;
-
-// @Scheduled(cron = "0/30 * * * * ?")
- public void orderConfirm() throws SerializeException {
-
-// log.info("PayPal orderConfirm 被执行......");
-
- List orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.PAYPAL.getType());
-
- for (OrderInfo orderInfo : orderInfoList) {
- String orderNo = orderInfo.getOrderNo();
- log.warn("超时订单 ===> {}", orderNo);
-
- //核实订单状态:调用支付宝查单接口
- payPalCheckoutService.checkOrderStatus(orderNo);
-
- }
- }
-}
diff --git a/src/main/java/com/ai/da/common/task/StripeTask.java b/src/main/java/com/ai/da/common/task/StripeTask.java
deleted file mode 100644
index d4a6fbaf..00000000
--- a/src/main/java/com/ai/da/common/task/StripeTask.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.ai.da.common.task;
-
-import com.ai.da.common.enums.PayTypeEnum;
-import com.ai.da.mapper.primary.entity.OrderInfo;
-import com.ai.da.service.OrderInfoService;
-import com.ai.da.service.StripeService;
-import com.paypal.http.exceptions.SerializeException;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Resource;
-import java.util.List;
-
-@Slf4j
-@Component
-public class StripeTask {
-
- @Resource
- private OrderInfoService orderInfoService;
-
- @Resource
- private StripeService stripeService;
-
-// @Scheduled(cron = "0/30 * * * * ?")
- public void orderConfirm() throws SerializeException {
-
- // 查看超过30分钟以上仍未支付的订单 置为超时订单
- List orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.STRIPE.getType());
-
- for (OrderInfo orderInfo : orderInfoList) {
- String orderNo = orderInfo.getOrderNo();
- log.warn("超时订单 ===> {}", orderNo);
-
- //核实订单状态:调用支付宝查单接口
- stripeService.checkOrderStatus(orderNo);
-
- }
- }
-}
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 a89115f8..d1a30ae1 100644
--- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
+++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
@@ -2,8 +2,10 @@ 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.SubscriptionEmailParamsDTO;
import com.alibaba.fastjson.JSONObject;
import com.ai.da.common.config.exception.BusinessException;
+import com.alibaba.fastjson2.JSON;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
@@ -767,4 +769,94 @@ public class SendEmailUtil {
throw new BusinessException("failed.to.send.mail");
}
}
+
+ private final static Long CANCEL_MERCHANT_EN = 130720L;
+ private final static Long NEW_MERCHANT_EN = 130721L;
+ private final static Long NEW_USER_EN = 130722L;
+ private final static Long NEW_USER_CN = 130723L;
+ private final static Long RENEWAL_MERCHANT_EN = 130724L;
+ private final static Long RENEWAL_USER_EN = 130725L;
+ private final static Long RENEWAL_USER_CN = 130726L;
+ private final static Long RENEWAL_REMINDER_USER_EN = 130727L;
+ private final static Long RENEWAL_REMINDER_USER_CN = 130728L;
+
+ public static void subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress){
+ try{
+ String kimEmail = "kimwong@code-create.com.hk";
+ 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 user = new SendEmailRequest();
+ user.setFromEmailAddress(SEND_ADDRESS);
+ user.setDestination(new String[]{receiverAddress});
+ SendEmailRequest merchant = new SendEmailRequest();
+ merchant.setFromEmailAddress(SEND_ADDRESS);
+ merchant.setDestination(new String[]{kimEmail});
+ Template templateUser = new Template();
+ Template templateMerchant = new Template();
+ switch (type) {
+ case "cancel":
+ merchant.setSubject("[Code-Create] Subscription Cancelled");
+ templateMerchant.setTemplateID(CANCEL_MERCHANT_EN);
+ break;
+ case "new":
+ merchant.setSubject("[Code-Create] New Order(" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateMerchant.setTemplateID(NEW_MERCHANT_EN);
+ if (language.equals("ENGLISH")){
+ user.setSubject("[Code-Create] You have successfully subscribed to AiDA");
+ templateUser.setTemplateID(NEW_USER_EN);
+ }else {
+ user.setSubject("[Code-Create] 您已成功订阅AiDA");
+ templateUser.setTemplateID(NEW_USER_CN);
+ }
+ break;
+ case "renewal":
+ merchant.setSubject("[Code-Create] New subscription renewal order (" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateMerchant.setTemplateID(RENEWAL_MERCHANT_EN);
+ if (language.equals("ENGLISH")){
+ user.setSubject("[Code-Create] AiDA Renewal Successful");
+ templateUser.setTemplateID(RENEWAL_USER_EN);
+ }else {
+ user.setSubject("[Code-Create] AiDA续订成功");
+ templateUser.setTemplateID(RENEWAL_USER_CN);
+ }
+ break;
+ case "reminder":
+ if (language.equals("ENGLISH")){
+ user.setSubject("[Code-Create] AiDA Subscription Renewal Reminder");
+ templateUser.setTemplateID(RENEWAL_REMINDER_USER_EN);
+ }else {
+ user.setSubject("[Code-Create] AiDA续订提醒");
+ templateUser.setTemplateID(RENEWAL_REMINDER_USER_CN);
+ }
+ break;
+ default:
+ log.error("unknown subscription email type");
+// throw new BusinessException("unknown subscription email type");
+ }
+
+ templateUser.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO));
+ user.setTemplate(templateUser);
+
+ templateMerchant.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO));
+ merchant.setTemplate(templateMerchant);
+
+ // 返回的resp是一个SendEmailResponse的实例,与请求对象对应
+ SendEmailResponse respUser = client.SendEmail(user);
+ // todo 暂时先不发商家邮件
+// SendEmailResponse respMerchant = client.SendEmail(merchant);
+ log.info("邮件发送结果toUser###{}", SendEmailResponse.toJsonString(respUser));
+// log.info("邮件发送结果toMerchant###{}", SendEmailResponse.toJsonString(respMerchant));
+ } catch (TencentCloudSDKException e) {
+ log.info("邮件发送失败###{}", e.toString());
+ throw new BusinessException("failed.to.send.mail");
+ }
+ }
}
diff --git a/src/main/java/com/ai/da/controller/StripeController.java b/src/main/java/com/ai/da/controller/StripeController.java
index 70430fab..eac49851 100644
--- a/src/main/java/com/ai/da/controller/StripeController.java
+++ b/src/main/java/com/ai/da/controller/StripeController.java
@@ -1,17 +1,21 @@
package com.ai.da.controller;
import com.ai.da.common.response.Response;
+import com.ai.da.model.dto.ProductPurchaseDTO;
import com.ai.da.service.StripeService;
import com.paypal.http.HttpResponse;
import com.paypal.payments.Refund;
+import com.stripe.exception.StripeException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
+import org.simpleframework.xml.core.Validate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Api(tags = "Stripe模块")
@@ -24,19 +28,19 @@ public class StripeController {
private StripeService stripeService;
@ApiOperation("创建支付链接")
- @PostMapping("/createOrder/{amount}")
- public Response pay(@PathVariable Integer amount, @RequestParam String returnUrl) {
- return Response.success(stripeService.pay(amount, returnUrl));
+ @PostMapping("/createOrder")
+ public Response pay(@Validate @RequestBody ProductPurchaseDTO productPurchaseDTO) {
+ return Response.success(stripeService.pay(productPurchaseDTO));
}
@ApiOperation("支付通知")
@PostMapping("/trade/notify")
- public Response callback(HttpServletRequest request) throws ServletException, IOException {
+ public void callback(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Boolean result = stripeService.notify(request);
if (result){
- return Response.success(200,"success");
+ response.setStatus(HttpServletResponse.SC_OK);
}else {
- return Response.fail(400,"failure");
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
@@ -51,4 +55,20 @@ public class StripeController {
}
}
+ @ApiOperation("获取订阅")
+ @PostMapping("/getSubscription")
+ public void getSubscription() {
+ try {
+ stripeService.getSubscription("xp", "xupei3360@163.com");
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @ApiOperation("取消订阅")
+ @PostMapping("/cancelSubscription")
+ public Response cancelSubscription(@RequestParam String subscriptionId) {
+ stripeService.cancelSubscription(subscriptionId);
+ return Response.success("success");
+ }
}
diff --git a/src/main/java/com/ai/da/mapper/primary/SubscriptionInfoMapper.java b/src/main/java/com/ai/da/mapper/primary/SubscriptionInfoMapper.java
new file mode 100644
index 00000000..8a45bb7e
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/SubscriptionInfoMapper.java
@@ -0,0 +1,8 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.mapper.primary.entity.SubscriptionInfo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface SubscriptionInfoMapper extends BaseMapper {
+
+}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
index c3876106..3806d888 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
@@ -2,7 +2,9 @@ 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_payment_info")
public class PaymentInfo extends BaseEntity{
@@ -13,11 +15,15 @@ public class PaymentInfo extends BaseEntity{
private String paymentType;//支付类型
- private String tradeType;//交易类型
-
private String tradeState;//交易状态
private Float payerTotal;//支付金额(元)
private String content;//通知参数
+
+ // 支付类型 new || renewal
+ private String type;
+
+ // 当前支付是否已邮件通知 0 || 1
+ private Integer notified;
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
new file mode 100644
index 00000000..2b61cf48
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
@@ -0,0 +1,43 @@
+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_subscription_info")
+public class SubscriptionInfo extends BaseEntity{
+
+ private Long accountId;
+
+ private String orderNo;
+
+ // stripe || paypal 平台生成的id
+ private String subscriptionId;
+
+ // month || year
+ private String type;
+
+ // active || expired
+ private String status = "active";
+
+ // 是否自动续订
+ private byte autoRenewal = (byte)1;
+
+ // 支付方式
+ private String paymentMethod;
+
+ // 如果是用卡支付,可以看到银行卡最后四位
+ private String last4;
+
+ // 续订的下一个付款日
+ private String nextPayDate;
+
+ // 当前订阅订单有效期开始时间
+ private Long currentPeriodStart;
+
+ // 当前订阅订单有效期结束时间
+ private Long currentPeriodEnd;
+
+}
diff --git a/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java b/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
new file mode 100644
index 00000000..ce1fb595
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
@@ -0,0 +1,32 @@
+package com.ai.da.model.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+@ApiModel("购买产品DTO")
+public class ProductPurchaseDTO {
+
+ @ApiModelProperty("购买数量")
+ private int quantity;
+
+ // http://example.com
+ @NotBlank(message = "return url cannot be empty")
+ @ApiModelProperty("购买完成后返回页面地址")
+ private String returnUrl;
+
+ @NotBlank(message = "product name cannot be empty")
+ @ApiModelProperty("产品名 CreditsPurchase || Subscription")
+ private String productName;
+
+ @ApiModelProperty("Month || Year")
+ private String subscribeType;
+
+ @ApiModelProperty("是否自动续订 one_time || recurring")
+ private Boolean autoRenewal;
+
+ private String refId;
+}
diff --git a/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
new file mode 100644
index 00000000..0020582f
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
@@ -0,0 +1,47 @@
+package com.ai.da.model.dto;
+
+import lombok.Data;
+
+@Data
+public class SubscriptionEmailParamsDTO {
+ // 用户名
+ private String username;
+
+ // t_payment_info id(每次支付对于用户来说是一笔新订单)
+ private String orderId;
+
+ // 订单支付创建日期
+ private String createDate;
+
+ // 购买数量
+ private String quantity;
+
+ // 费用
+ private String totalFee;
+
+ // 当前订阅开始时间
+ private String lastOrderDate;
+
+ // 当前订阅结束时间
+ private String endOfPrepaidTerm;
+
+ // 付款方式
+ private String paymentMethod;
+
+ // 订阅Id
+ private String subscriptionId;
+
+ // 订阅方式
+ private String subscriptionType;
+
+ // 订阅开始时间
+ private String startDate;
+
+ // 下一个支付日期
+ private String nextPayDate;
+
+ // 下次付款时间(reminder)
+ private String renewalTime;
+
+
+}
diff --git a/src/main/java/com/ai/da/service/OrderInfoService.java b/src/main/java/com/ai/da/service/OrderInfoService.java
index 35da4881..a420fe70 100644
--- a/src/main/java/com/ai/da/service/OrderInfoService.java
+++ b/src/main/java/com/ai/da/service/OrderInfoService.java
@@ -2,6 +2,7 @@ package com.ai.da.service;
import com.ai.da.common.enums.OrderStatusEnum;
+import com.ai.da.common.enums.ProductEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.QueryPageByTimeDTO;
@@ -13,6 +14,8 @@ public interface OrderInfoService extends IService {
OrderInfo createOrderByProductId(Integer productId, String paymentType);
+ OrderInfo createOrderByProductId(Integer amount, String paymentType, ProductEnum product);
+
void saveCodeUrl(String orderNo, String codeUrl);
List listOrderByCreateTimeDesc();
@@ -28,4 +31,7 @@ public interface OrderInfoService extends IService {
PageBaseResponse getOrderByPage(QueryPageByTimeDTO queryPageByTimeDTO);
void updateOrderNoById(Long id, String orderNo);
+
+ void updateTotalFeeByOrderNo(String orderNo);
+
}
diff --git a/src/main/java/com/ai/da/service/PaymentInfoService.java b/src/main/java/com/ai/da/service/PaymentInfoService.java
index 74492683..6d3d2d5e 100644
--- a/src/main/java/com/ai/da/service/PaymentInfoService.java
+++ b/src/main/java/com/ai/da/service/PaymentInfoService.java
@@ -2,12 +2,13 @@ package com.ai.da.service;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
+import com.baomidou.mybatisplus.extension.service.IService;
import com.paypal.orders.Order;
-import com.stripe.model.checkout.Session;
+import com.stripe.model.Invoice;
import java.util.Map;
-public interface PaymentInfoService {
+public interface PaymentInfoService extends IService {
void createPaymentInfo(String plainText);
@@ -17,7 +18,7 @@ public interface PaymentInfoService {
void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO);
- void createPaymentInfoForStripe(Session session);
+ String createPaymentInfoForStripe(Invoice invoice);
PaymentInfo getPaymentInfoByOrderId(String orderId);
diff --git a/src/main/java/com/ai/da/service/StripeService.java b/src/main/java/com/ai/da/service/StripeService.java
index 98709597..b517b325 100644
--- a/src/main/java/com/ai/da/service/StripeService.java
+++ b/src/main/java/com/ai/da/service/StripeService.java
@@ -1,15 +1,35 @@
package com.ai.da.service;
+import com.ai.da.model.dto.ProductPurchaseDTO;
+import com.stripe.exception.StripeException;
+import com.stripe.model.Subscription;
+
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
public interface StripeService {
- String pay(Integer quantity, String returnUrl);
+ String pay(ProductPurchaseDTO productPurchaseDTO);
Boolean notify(HttpServletRequest request);
String refund(String amount, String orderId, String reason);
void checkOrderStatus(String orderNo);
+
+ List getSubscription(String name, String userEmail) throws StripeException;
+
+ void cancelSubscription(String orderNo);
+
+ Map getPaymentMethod(String paymentMethodId);
+
+ /*void updateSubscription(String subscriptionId);
+
+ void resume(String subscriptionId);*/
+
+ void subscriptionReminder();
+
+ void checkSubscriptionExpiration();
+
}
diff --git a/src/main/java/com/ai/da/service/impl/OrderInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/OrderInfoServiceImpl.java
index 416d84d2..ae12e612 100644
--- a/src/main/java/com/ai/da/service/impl/OrderInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/OrderInfoServiceImpl.java
@@ -4,15 +4,19 @@ package com.ai.da.service.impl;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.OrderStatusEnum;
+import com.ai.da.common.enums.ProductEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.OrderNoUtils;
import com.ai.da.mapper.primary.OrderInfoMapper;
+import com.ai.da.mapper.primary.PaymentInfoMapper;
import com.ai.da.mapper.primary.ProductMapper;
import com.ai.da.mapper.primary.entity.OrderInfo;
+import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.QueryPageByTimeDTO;
import com.ai.da.model.vo.AuthPrincipalVo;
import com.ai.da.service.OrderInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
@@ -34,9 +38,13 @@ public class OrderInfoServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_no", orderNo);
OrderInfo orderInfo = baseMapper.selectOne(queryWrapper);
- if(orderInfo == null){
+ if (orderInfo == null) {
return null;
}
return orderInfo.getOrderStatus();
@@ -129,6 +169,7 @@ public class OrderInfoServiceImpl extends ServiceImpl getOrderByPage(QueryPageByTimeDTO queryPageByTimeDTO){
+ public PageBaseResponse getOrderByPage(QueryPageByTimeDTO queryPageByTimeDTO) {
QueryWrapper qw = new QueryWrapper<>();
- qw.eq("account_id",UserContext.getUserHolder().getId());
+ qw.eq("account_id", UserContext.getUserHolder().getId());
String startTime = queryPageByTimeDTO.getStartTime();
String endTime = queryPageByTimeDTO.getEndTime();
- if (StringUtil.isNullOrEmpty(startTime)){
+ if (StringUtil.isNullOrEmpty(startTime)) {
startTime = "2024-02-01 00:00:00";
}
- if (StringUtil.isNullOrEmpty(endTime)){
+ if (StringUtil.isNullOrEmpty(endTime)) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
endTime = now.format(dateTimeFormatter);
@@ -206,11 +249,28 @@ public class OrderInfoServiceImpl extends ServiceImpl qw = new QueryWrapper<>();
+ qw.eq("order_no", orderNo);
+ List paymentInfos = paymentInfoMapper.selectList(qw);
+ Float sum = paymentInfos.stream()
+ .map(PaymentInfo::getPayerTotal)
+ .reduce(0f, Float::sum);
+
+ baseMapper.update(
+ new OrderInfo(),
+ new UpdateWrapper()
+ .eq("order_no", orderNo)
+ .set("total_fee", sum)
+ );
+ }
+
}
diff --git a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
index 2a80a4f5..035e1135 100644
--- a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
@@ -9,14 +9,20 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.paypal.orders.Order;
+import com.stripe.Stripe;
+import com.stripe.exception.StripeException;
+import com.stripe.model.Invoice;
+import com.stripe.model.Subscription;
import com.stripe.model.checkout.Session;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
@Service
@Slf4j
@@ -50,7 +56,6 @@ public class PaymentInfoServiceImpl extends ServiceImpl qw = new QueryWrapper<>();
+ qw.eq("transaction_id", invoiceId);
+ PaymentInfo paymentInfo = baseMapper.selectOne(qw);
+ // 判断当前支付是否已经被记录,确保同一个支付不会被重复记录
+ if (Objects.isNull(paymentInfo)){
+ String orderNo;
+ String subscriptionId = invoice.getSubscription();
+ try {
+ // 从subscription中获取orderNo
+ orderNo = Subscription.retrieve(subscriptionId).getDescription().replace("AiDA - ", "");
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+ String status = invoice.getStatus();
+ Long amountTotal = invoice.getAmountPaid();
+ // stripe 的支付金额单位是分,在我们数据库中金额单位为 元
+ Float divide = new BigDecimal(amountTotal).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).floatValue();
+ String type = invoice.getBillingReason().equals("subscription_create") ? "new" :
+ invoice.getBillingReason().equals("subscription_cycle") ? "renewal" : invoice.getBillingReason();
+
+
+ paymentInfo = new PaymentInfo();
+ paymentInfo.setOrderNo(orderNo);
+ paymentInfo.setPaymentType(PayTypeEnum.STRIPE.getType());
+ paymentInfo.setTransactionId(invoiceId);
+ // 使用invoice的状态
+ paymentInfo.setTradeState(status);
+ paymentInfo.setPayerTotal(divide);
+ Gson gson = new Gson();
+ String json = gson.toJson(invoice);
+ paymentInfo.setContent(json);
+ paymentInfo.setType(type);
+ paymentInfo.setNotified(0);
+
+ baseMapper.insert(paymentInfo);
+ return orderNo;
+ }
+ return null;
+ }
+
+
@Override
public PaymentInfo getPaymentInfoByOrderId(String orderId){
QueryWrapper qw = new QueryWrapper<>();
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 7951adcd..b5e3a68e 100644
--- a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java
@@ -1,16 +1,22 @@
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.enums.AliPayTradeStateEnum;
-import com.ai.da.common.enums.CreditsEventsEnum;
-import com.ai.da.common.enums.OrderStatusEnum;
-import com.ai.da.common.enums.PayTypeEnum;
-import com.ai.da.common.utils.OrderNoUtils;
+import com.ai.da.common.enums.*;
+import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AccountMapper;
+import com.ai.da.mapper.primary.PaymentInfoMapper;
+import com.ai.da.mapper.primary.SubscriptionInfoMapper;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.mapper.primary.entity.RefundInfo;
+import com.ai.da.mapper.primary.entity.SubscriptionInfo;
+import com.ai.da.model.dto.ProductPurchaseDTO;
+import com.ai.da.model.dto.SubscriptionEmailParamsDTO;
import com.ai.da.service.*;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.gson.Gson;
import com.stripe.Stripe;
@@ -21,6 +27,7 @@ import com.stripe.model.checkout.Session;
import com.stripe.net.Webhook;
import com.stripe.param.*;
import com.stripe.param.checkout.SessionCreateParams;
+import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -30,10 +37,13 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+@SuppressWarnings("LoggingSimilarMessage")
@Service
@Slf4j
public class StripeServiceImpl implements StripeService {
@@ -51,6 +61,10 @@ public class StripeServiceImpl implements StripeService {
@Resource
private AccountMapper accountMapper;
+ @Resource
+ private SubscriptionInfoMapper subscriptionInfoMapper;
+ @Resource
+ private PaymentInfoMapper paymentInfoMapper;
@Value("${stripe.private-key}")
private String privateKey;
@@ -60,53 +74,78 @@ public class StripeServiceImpl implements StripeService {
@Override
@Transactional(rollbackFor = Exception.class)
- public String pay(Integer quantity, String returnUrl) {
+ public String pay(ProductPurchaseDTO productPurchaseDTO) {
Stripe.apiKey = privateKey;
- log.info("生成订单");
- OrderInfo orderInfo = orderInfoService.createOrderByProductId(quantity, PayTypeEnum.STRIPE.getType());
- Long id = UserContext.getUserHolder().getId();
- com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(id);
+ ProductEnum productEnum;
+ switch (productPurchaseDTO.getProductName()){
+ case "Credits Purchase":
+ productEnum = ProductEnum.CreditsProduct;
+ break;
+ case "Subscription":
+ switch (productPurchaseDTO.getSubscribeType()){
+ case "Month":
+ productEnum = ProductEnum.MonthlySubscription;
+ break;
+ case "Year":
+ productEnum = ProductEnum.AnnualSubscription;
+ break;
+ case "Day":
+ productEnum = ProductEnum.DailySubscription;
+ break;
+ default:
+ throw new BusinessException("unknown subscription type");
+ }
+ break;
+ default:
+ throw new BusinessException("unknown product type");
+ }
+ log.info("生成订单");
+ OrderInfo orderInfo = orderInfoService.createOrderByProductId(productPurchaseDTO.getQuantity(), PayTypeEnum.STRIPE.getType(), productEnum);
+ String payType;
+ if (productPurchaseDTO.getAutoRenewal()){
+ payType = "recurring";
+ }else {
+ payType = "one_time";
+ }
try {
- //创建产品
- Map params = new HashMap<>();
- params.put("name", "AiDA credits purchase");
- Product product = Product.create(params);
- String orderId = OrderNoUtils.getOrderNo();
-
- BigDecimal actualAmount = new BigDecimal(Long.parseLong(CreditsEventsEnum.PRICE.getValue()) * 100); //stripe的默认单位是分,即传入的amount实际上小数点会被左移两位
- //给price绑定元数据并更新price用于检索
- Map metadata = new HashMap<>();
- metadata.put("orderId", orderId);
- //创建价格
- Map priceParams = new HashMap<>();
- priceParams.put("metadata", metadata); //通过订单号关联用于检索price信息(可选)
- priceParams.put("unit_amount", actualAmount.intValue());
- priceParams.put("currency", "HKD");
- priceParams.put("product", product.getId());
- Price price = Price.create(priceParams);
+ Long id = UserContext.getUserHolder().getId();
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(id);
+ // 获取或创建产品
+ String productId = getProduct(productEnum.getName());
+ // 获取或创建价格
+ String priceId = getPrice(productEnum.getPrice(), productId, payType, productPurchaseDTO.getSubscribeType());
+ // 获取或创建customer
+ String customerId = getCustomer(account.getUserName(), account.getUserEmail());
+ // 获取自定义订单号
+ String orderId = orderInfo.getOrderNo();
//创建支付信息得到url
- SessionCreateParams params3 = SessionCreateParams.builder()
- .setMode(SessionCreateParams.Mode.PAYMENT)
- .setSuccessUrl(returnUrl)//可自定义成功页面
- .setPaymentIntentData(SessionCreateParams.PaymentIntentData.builder().setDescription("AiDA - " + orderId).build())
- .setLocale(account.getLanguage().equals("CHINESE_SIMPLIFIED") ? SessionCreateParams.Locale.ZH : SessionCreateParams.Locale.EN)
- .addLineItem(
- SessionCreateParams.LineItem.builder()
- .setQuantity(Long.valueOf(quantity))
- .setPrice(price.getId())
- .build())
- .putMetadata("orderId", orderId) //通过订单号关联用于检索支付信息(可选)
- .build();
- Session session = Session.create(params3);
+ // 一次性支付和周期扣款,需要区分mode: payment || subscription || setup
+ SessionCreateParams.Builder sessionBuilder = new SessionCreateParams.Builder();
+ if (payType.equals("recurring")){
+ sessionBuilder.setMode(SessionCreateParams.Mode.SUBSCRIPTION);
+ sessionBuilder.setSubscriptionData(SessionCreateParams.SubscriptionData.builder().setDescription("AiDA - " + orderId).build());
+ }else {
+ sessionBuilder.setMode(SessionCreateParams.Mode.PAYMENT);
+ sessionBuilder.setPaymentIntentData(SessionCreateParams.PaymentIntentData.builder().setDescription("AiDA - " + orderId).build());
+ }
+ sessionBuilder.setCustomer(customerId);
+ sessionBuilder.setSuccessUrl(productPurchaseDTO.getReturnUrl());//可自定义成功页面
+ sessionBuilder.setLocale(account.getLanguage().equals("CHINESE_SIMPLIFIED") ? SessionCreateParams.Locale.ZH : SessionCreateParams.Locale.EN);
+ sessionBuilder.addLineItem(
+ SessionCreateParams.LineItem.builder()
+ .setQuantity((long) productPurchaseDTO.getQuantity())
+ .setPrice(priceId)
+ .build());
+ sessionBuilder.putMetadata("orderId", orderId); //通过订单号关联用于检索支付信息(可选)
+
+ Session session = Session.create(sessionBuilder.build());
log.info("sessionId:" + session.getId()); //退款方式1:拿到sessionId入库,退款的时候根据这个id找到PaymentIntent的id然后发起退款
// 更新order信息
orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
- //记录支付日志
- paymentInfoService.createPaymentInfoForStripe(session);
return session.getUrl();
} catch (Exception e) {
log.error("创建支付会话出现异常:", e);
@@ -114,6 +153,69 @@ public class StripeServiceImpl implements StripeService {
return "";
}
+ // 获取产品ID
+ private String getProduct(String productName) throws StripeException {
+ Stripe.apiKey = privateKey;
+ // 1、获取所有的产品
+ ProductCollection productCollection = Product.list(ProductListParams.builder().setActive(Boolean.TRUE).build());
+ // 2、取一个指定名称的产品
+ for (Product product : productCollection.getData()) {
+ if (product.getName().equals(productName)) {
+ return product.getId();
+ }
+ }
+ // 3、在现有产品中没有找到指定产品,新建产品
+ Map params = new HashMap<>();
+ params.put("name", productName);
+ Product product = Product.create(params);
+ return product.getId();
+ }
+
+ /**
+ * 获取价格
+ *
+ * @param priceValue 价格值
+ * @param payType recurring || one_time
+ * @param recurringType monthly || yearly
+ */
+ private String getPrice(Long priceValue, String productId, String payType, String recurringType) throws StripeException {
+ Stripe.apiKey = privateKey;
+ PriceCollection priceCollection = Price.list(PriceListParams.builder()
+ .setActive(Boolean.TRUE)
+ .setProduct(productId).build());
+ for (Price price : priceCollection.getData()) {
+ // stripe的金额单位为分,所以这里需要 ×100
+ if (price.getUnitAmount().equals(priceValue * 100) && price.getType().equals(payType)) {
+ return price.getId();
+ }
+ }
+
+ Price price = createPrice(priceValue, productId, payType, recurringType);
+ return price.getId();
+ }
+
+ private Price createPrice(Long priceValue, String productId, String payType, String recurringType) throws StripeException {
+ BigDecimal actualAmount = new BigDecimal(priceValue * 100); //stripe的默认单位是分,即传入的amount实际上小数点会被左移两位
+
+ PriceCreateParams.Builder priceCreateParams = new PriceCreateParams.Builder();
+ priceCreateParams.setBillingScheme(PriceCreateParams.BillingScheme.PER_UNIT);
+ priceCreateParams.setCurrency("HKD");
+ priceCreateParams.setProduct(productId);
+ priceCreateParams.setUnitAmount(actualAmount.longValue());
+
+ if (payType.equals("recurring")) {
+ PriceCreateParams.Recurring.Builder recurring = new PriceCreateParams.Recurring.Builder();
+ // One of day, week, month or year.
+ recurring.setInterval(PriceCreateParams.Recurring.Interval.valueOf(recurringType.toUpperCase()));
+ // The number of intervals (specified in the interval attribute) between subscription billings.
+ // For example, interval=month and interval_count=3 bills every 3 months.
+ recurring.setIntervalCount(1L);
+ priceCreateParams.setRecurring(recurring.build());
+ }
+
+ return Price.create(priceCreateParams.build());
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean notify(HttpServletRequest request) {
@@ -127,19 +229,19 @@ public class StripeServiceImpl implements StripeService {
} catch (Exception e) {
log.info("stripe 支付回调参数解析异常:errorMsg {}", e.getMessage());
log.info("request sigHeader = {}", sigHeader);
- log.info("request body = {}", payload);
+ log.info("request body = {}", JSON.toJSONString(payload));
e.printStackTrace();
return Boolean.FALSE;
}
- Event event = null;
+ Event event;
try {
assert sigHeader != null;
event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
} catch (SignatureVerificationException e) {
- log.info("stripe 验签,获取事件异常, errorMsg=" + e.getMessage());
+ log.info("stripe 验签,获取事件异常, errorMsg={}", e.getMessage());
log.info("request sigHeader = {}", sigHeader);
- log.info("request body = {}", payload);
+ log.info("request body = {}", JSON.toJSONString(payload));
e.printStackTrace();
return Boolean.FALSE;
}
@@ -148,23 +250,65 @@ public class StripeServiceImpl implements StripeService {
// Deserialize the nested object inside the event
assert event != null;
EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
- StripeObject stripeObject = null;
+ StripeObject stripeObject ;
if (dataObjectDeserializer.getObject().isPresent()) {
stripeObject = dataObjectDeserializer.getObject().get();
} else {
log.info("stripe 验签失败!");
log.info("request sigHeader = {}", sigHeader);
- log.info("request body = {}", payload);
+ log.info("request body = {}", JSON.toJSONString(payload));
return Boolean.FALSE;
}
log.info("stripe验签成功");
+ Boolean response = Boolean.TRUE;
- if (event.getType().equals("checkout.session.completed")) {
+ log.info("回调事件 {}", event.getType());
+ if (stripeObject instanceof Session){
Session session = (Session) stripeObject;
- processOrder(session);
- }
+ if (event.getType().equals("checkout.session.completed")) {
+ processOrder(session);
+ }
+ } else if (stripeObject instanceof Subscription){
+ Subscription subscription = (Subscription) stripeObject;
+ if (event.getType().equals("customer.subscription.created")){
+ // 添加数据到t_subscription_info表 需记录订阅id。需要判断订阅的状态是否active吗 ??
+ createSubscription(subscription);
+ log.info("创建连续订阅");
+ } else if (event.getType().equals("customer.subscription.updated")){
+ // 更新订阅信息
+ response = updateSubscription(subscription);
+ log.info("订阅更新");
+ if (subscription.getStatus().equals("active")){
+ response = sendEmail(subscription.getId(), null);
+ }
+ } else if (event.getType().equals("customer.subscription.deleted")){
+ response = updateSubscription(subscription);
+ log.info("用户取消连续订阅");
+ } else if (event.getType().equals("customer.subscription.paused")){
+ updateSubscription(subscription);
+ } else if (event.getType().equals("customer.subscription.resumed")){
+ updateSubscription(subscription);
+ log.info("用户订阅恢复");
+ }
+ } else if (stripeObject instanceof Invoice) {
+ Invoice invoice = (Invoice) stripeObject;
+ if (event.getType().equals("invoice.paid")) {
+ // 新增支付成功的信息,返回orderNo,表示,该回调第一次被记录
+ String orderNo = paymentInfoService.createPaymentInfoForStripe(invoice);
- return Boolean.TRUE;
+ if (!StringUtil.isNullOrEmpty(orderNo)) {
+ // 更新t_order_info中的total_fee,记录该订单的累计付款金额
+ orderInfoService.updateTotalFeeByOrderNo(orderNo);
+ // 邮件通知商家和用户
+ if (invoice.getBillingReason().equals("subscription_create")){
+ response = sendEmail(invoice.getSubscription(), "new");
+ } else if (invoice.getBillingReason().equals("subscription_cycle")){
+ response = sendEmail(invoice.getSubscription(), "renewal");
+ }
+ }
+ }
+ }
+ return response;
}
public void processOrder(Session session) {
@@ -186,31 +330,135 @@ public class StripeServiceImpl implements StripeService {
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
log.info("Stripe 订单:{} 状态更新成功", orderId);
- // 更新支付日志
- paymentInfoService.updatePaymentStatusById(
- paymentInfoService.getPaymentInfoByOrderId(orderId).getId(),
- session.getStatus(),
- new Gson().toJson(session));
-
- log.info("Stripe 订单:{} 支付信息状态更新成功", orderId);
- float quantity = totalAmount / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
- // 更新积分
- creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
- // 添加积分变更记录
- creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
- CreditsEventsEnum.BUY_CREDITS.getName() + "--Stripe",
- String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
- log.info("用户:{} 积分信息更新成功", orderByOrderNo.getAccountId());
- }catch (Exception e){
+ if (orderByOrderNo.getTitle().startsWith("积分购买")){
+ float quantity = totalAmount / ProductEnum.CreditsProduct.getPrice();
+ // 更新积分
+ creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
+ // 添加积分变更记录
+ creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
+ CreditsEventsEnum.BUY_CREDITS.getName() + "--Stripe",
+ String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
+ "positive");
+ log.info("用户:{} 积分信息更新成功", orderByOrderNo.getAccountId());
+ }
+ } catch (Exception e) {
log.info(e.getMessage());
}
}
+ @Transactional(rollbackFor = Exception.class)
+ public SubscriptionInfo createSubscription(Subscription subscription){
+ // 确认当前subscription是否已经记录
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.eq("subscription_id", subscription.getId());
+ SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qw);
+ if (Objects.isNull(subscriptionInfo)){
+ String description = subscription.getDescription();
+ String orderNo = description.replace("AiDA - ", "");
+ OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
+
+ // 从回调信息中获取recurring type
+ SubscriptionItem subscriptionItem = subscription.getItems().getData().get(0);
+ String interval = subscriptionItem.getPrice().getRecurring().getInterval();
+ Map paymentMethod = getPaymentMethodByInvoiceId(subscription.getLatestInvoice());
+
+ subscriptionInfo = new SubscriptionInfo();
+ subscriptionInfo.setAccountId(orderInfo.getAccountId());
+ subscriptionInfo.setOrderNo(orderNo);
+ subscriptionInfo.setSubscriptionId(subscription.getId());
+ subscriptionInfo.setType(interval);
+ subscriptionInfo.setStatus(subscription.getStatus());
+ subscriptionInfo.setPaymentMethod(paymentMethod.get("paymentMethod"));
+ subscriptionInfo.setLast4(paymentMethod.get("last4"));
+ subscriptionInfo.setNextPayDate(changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
+ subscriptionInfo.setCurrentPeriodStart(subscription.getCurrentPeriodStart());
+ subscriptionInfo.setCurrentPeriodEnd(subscription.getCurrentPeriodEnd());
+ subscriptionInfo.setCreateTime(LocalDateTime.now());
+ subscriptionInfoMapper.insert(subscriptionInfo);
+
+ // 更新账号到期时间
+ updateAccountValidity(subscriptionInfo.getAccountId(), subscriptionInfo.getCurrentPeriodEnd());
+ }
+ return subscriptionInfo;
+ }
+
+ public String changeTimeStampFormat(Long timeStamp, String type, String format){
+ // 将秒级时间戳转换为毫秒级
+ if (type.equals("seconds")){
+ timeStamp = timeStamp * 1000;
+ }
+ // 输出格式
+ SimpleDateFormat outputFormat = new SimpleDateFormat(format, Locale.ENGLISH);
+ // 创建Date对象
+ Date date = new Date(timeStamp);
+ // 格式化输出
+ return outputFormat.format(date);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean updateSubscription(Subscription subscription){
+ // 获取当前是否有已经记录的subscriptionInfo
+ SubscriptionInfo subscriptionInfo = createSubscription(subscription);
+ // 用于标志数据有没有变动,避免在没有改动的情况下频繁的更新数据库
+ boolean flag = false;
+ if (!subscriptionInfo.getStatus().equals(subscription.getStatus())){
+ subscriptionInfo.setStatus(subscription.getStatus());
+ flag = true;
+ }
+ if (subscription.getStatus().equals("canceled")){
+ subscriptionInfo.setAutoRenewal((byte) 0);
+ }
+ if (!subscriptionInfo.getCurrentPeriodStart().equals(subscription.getCurrentPeriodStart())){
+ subscriptionInfo.setCurrentPeriodStart(subscription.getCurrentPeriodStart());
+ flag = true;
+ }
+ if (!subscriptionInfo.getCurrentPeriodEnd().equals(subscription.getCurrentPeriodEnd())){
+ subscriptionInfo.setCurrentPeriodEnd(subscription.getCurrentPeriodEnd());
+ subscriptionInfo.setNextPayDate(changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
+ // 更新账号到期时间
+ updateAccountValidity(subscriptionInfo.getAccountId(), subscriptionInfo.getCurrentPeriodEnd());
+ log.info("更新 {} 账号到期时间为:{}", subscriptionInfo.getAccountId(), changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
+ flag = true;
+ }
+ if (flag){
+ subscriptionInfo.setUpdateTime(LocalDateTime.now());
+ // todo 这里需要再检查支付方式吗?
+ subscriptionInfoMapper.updateById(subscriptionInfo);
+ }
+ return true;
+ }
+
+ private void updateAccountValidity(Long accountId, Long currentPeriodEnd){
+ // 不管当前用户的账号是否到期,都根据付款信息重置账号到期时间
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(accountId);
+ account.setValidEndTime(currentPeriodEnd * 1000);
+
+ accountMapper.updateById(account);
+ }
+
+ // 取消连续订阅 将订阅从pause状态转为cancel状态(使用定时器,定期检索DB中,过期且不续订的订阅)
+ public void cancelSubscription(String subscriptionId) {
+ Stripe.apiKey = privateKey;
+ Long accountId = UserContext.getUserHolder().getId();
+ log.info("用户 {} 申请取消连续订阅 {}", accountId, subscriptionId);
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(accountId);
+ List subscriptions = getSubscription(account.getUserName(), account.getUserEmail());
+ // 获取status = active的订阅
+ subscriptions.forEach(subscription -> {
+ if (subscription.getId().equals(subscriptionId)) {
+ try {
+ Subscription cancel = subscription.cancel();
+ cancel.getStatus();
+ } catch (StripeException e) {
+ log.error("订阅 {} 取消失败, error message : {}", subscription.getId(), e.getMessage());
+ }
+ }
+ });
+ }
public String refund(String amount, String orderId, String reason) {
- Refund refund ;
+ Refund refund;
RefundInfo refundByOrderNo = refundInfoService.createRefundByOrderNo(orderId, reason);
try {
@@ -235,7 +483,7 @@ public class StripeServiceImpl implements StripeService {
refund = Refund.create(params);
log.info("根据会话编号退款成功");
- }else {
+ } else {
log.error("当前订单不存在");
return "退款异常";
}
@@ -260,7 +508,7 @@ public class StripeServiceImpl implements StripeService {
// 更新积分状态
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderId);
- creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())));
+ creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int) (orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())));
} else {
//更新订单状态
orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_ABNORMAL);
@@ -275,17 +523,17 @@ public class StripeServiceImpl implements StripeService {
log.info("记录退款订单");
return "退款成功";
}
-
- public void checkOrderStatus(String orderNo){
+
+ public void checkOrderStatus(String orderNo) {
Stripe.apiKey = privateKey;
// 1、通过orderNo 查询sessionId
PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderId(orderNo);
try {
Session session = Session.retrieve(paymentInfo.getTransactionId());
- if (Objects.isNull(session)){
+ if (Objects.isNull(session)) {
log.warn("核实订单未创建 ===> {}", orderNo);
return;
- } else if (session.getStatus().equals("open") || session.getStatus().equals("expired")){
+ } else if (session.getStatus().equals("open") || session.getStatus().equals("expired")) {
// 订单未支付 || 订单过期 ---> 均设置为超时未支付
log.info("订单超时未支付 ===> {}", orderNo);
//更新本地订单状态
@@ -304,96 +552,195 @@ public class StripeServiceImpl implements StripeService {
}
- // 1、创建customer,获取customerId
- // 2、创建客户支付方式 (从前端获取)
- // 3、创建支付 paymentIntent
- // 4、确认订单
- // 5、捕获金额(执行扣款操作)
- public String createCustomer() throws StripeException {
+ // 获取所有订阅
+ public List getSubscription(String username, String userEmail) {
+ Stripe.apiKey = privateKey;
+ String customerId = null;
+ try {
+ customerId = getCustomer(username, userEmail);
+ SubscriptionCollection list = Subscription.list(SubscriptionListParams.builder()
+ .setCustomer(customerId).build());
+ return list.getData();
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private String getCustomer(String username, String userEmail) throws StripeException {
+ CustomerCollection list = Customer.list(CustomerListParams.builder().setEmail(userEmail).build());
+ List data = list.getData();
+ if (!data.isEmpty()) {
+ return data.get(0).getId();
+ }
+ return createCustomer(username, userEmail);
+ }
+
+ private String createCustomer(String name, String userEmail) throws StripeException {
Stripe.apiKey = privateKey;
// Customer允许重复使用
CustomerCreateParams params =
CustomerCreateParams.builder()
- .setName("xp")
- .setEmail("xupei3360@163.com")
+ .setName(name)
+ .setEmail(userEmail)
.build();
-
Customer customer = Customer.create(params);
return customer.getId();
}
- public String createPaymentMethod(String customerId) throws StripeException {
-
- Stripe.apiKey = privateKey;
-
- PaymentMethodCreateParams params =
- PaymentMethodCreateParams.builder()
- .setType(PaymentMethodCreateParams.Type.CARD)
- .setCard(
- // 测试中,不建议使用卡号,会不安全的异常,必须使用token(https://docs.stripe.com/testing?testing-method=tokens#visa)
- PaymentMethodCreateParams.Token.builder().setToken("tok_visa").build()
-// PaymentMethodCreateParams.CardDetails.builder()
-// .setNumber("4242424242424242")
-// .setExpMonth(8L)
-// .setExpYear(2026L)
-// .setCvc("314")
-// .build()
- )
- .build();
-
- PaymentMethod paymentMethod = PaymentMethod.create(params);
- return paymentMethod.getId();
+ /**
+ * 使用连续订阅的订单,回调中没有paymentIntentId,所以通过invoiceId间接获取
+ * @param invoiceId 发票Id
+ */
+ public Map getPaymentMethodByInvoiceId(String invoiceId) {
+ try {
+ Stripe.apiKey = privateKey;
+ Invoice invoice = Invoice.retrieve(invoiceId);
+ PaymentIntent paymentIntent = PaymentIntent.retrieve(invoice.getPaymentIntent());
+ PaymentMethod paymentMethod = PaymentMethod.retrieve(paymentIntent.getPaymentMethod());
+ return getPaymentMethod(paymentMethod.getId());
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
}
- public String createPaymentIntent(String paymentMethodId, String customerId) throws StripeException {
+ public Map getPaymentMethod(String paymentMethodId){
Stripe.apiKey = privateKey;
+ String paymentMethod = null;
+ String last4 = null;
- Long amount = 600L;
- PaymentIntentCreateParams params =
- PaymentIntentCreateParams.builder()
- .setAmount(amount)
-// .setPaymentMethod(paymentMethodId)
- .setCustomer(customerId)
- .setCurrency("hkd")
- .setAutomaticPaymentMethods(
- PaymentIntentCreateParams.AutomaticPaymentMethods.builder()
- .setEnabled(true)
- .build()
- )
- .build();
-
- PaymentIntent paymentIntent = PaymentIntent.create(params);
- return paymentIntent.getId();
+ try {
+ PaymentMethod retrieve = PaymentMethod.retrieve(paymentMethodId);
+ switch (retrieve.getType()){
+ case "alipay":
+ paymentMethod = "Alipay";
+ break;
+ case "bancontact":
+ paymentMethod = "BanContact";
+ break;
+ case "card":
+ PaymentMethod.Card card = retrieve.getCard();
+ String brand = card.getBrand();
+ brand = brand.substring(0, 1).toUpperCase() + brand.substring(1);
+ paymentMethod = brand + " " + card.getFunding() + "card";
+ last4 = card.getLast4();
+ break;
+ case "eps":
+ PaymentMethod.Eps eps = retrieve.getEps();
+ paymentMethod = eps.getBank();
+ break;
+ case "giropay":
+ paymentMethod = "GiroPay";
+ break;
+ case "ideal":
+ PaymentMethod.Ideal ideal = retrieve.getIdeal();
+ paymentMethod = ideal.getBank();
+ break;
+ case "link":
+ paymentMethod = "Link";
+ break;
+ }
+ HashMap resp = new HashMap<>();
+ resp.put("paymentMethod", paymentMethod);
+ resp.put("last4", last4);
+ return resp;
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+// return null;
}
- public String confirmPaymentIntent(String clientSecret) throws StripeException {
- Stripe.apiKey = privateKey;
+ public boolean sendEmail(String subscriptionId, String type){
- PaymentIntent resource = PaymentIntent.retrieve(clientSecret);
+ SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO();
+ QueryWrapper qwSI = new QueryWrapper<>();
+ qwSI.eq("subscription_id", subscriptionId);
+ SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qwSI);
+ if (Objects.isNull(subscriptionInfo)) {
+ return false;
+ }
+ QueryWrapper qwPI = new QueryWrapper<>();
+ qwPI.eq("order_no", subscriptionInfo.getOrderNo()).orderByDesc("id");
+ List paymentInfos = paymentInfoMapper.selectList(qwPI);
+ if (paymentInfos.isEmpty()) {
+ return false;
+ }
+ PaymentInfo paymentInfo = paymentInfos.get(0);
+ if (paymentInfo.getNotified() == 1){
+ // 已经邮件通知过,直接返回
+ return true;
+ }
+ if (StringUtil.isNullOrEmpty(type)){
+ // 如果没有传入type,则使用paymentInfo中记录的类型
+ // (其实这里也可以通过invoiceId查询stripe,但是记录在自己的db中可以不用每次都查,且方便查看)
+ type = paymentInfo.getType();
+ }
- PaymentIntentConfirmParams params =
- PaymentIntentConfirmParams.builder()
- .setPaymentMethod("pm_card_visa")
- .setReturnUrl("https://www.example.com")
- .build();
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(subscriptionInfo.getAccountId());
+ String userName = account.getUserName();
+ String language = account.getLanguage();
- PaymentIntent paymentIntent = resource.confirm(params);
- return paymentIntent.getId();
+ emailParamsDTO.setUsername(userName);
+ emailParamsDTO.setOrderId(paymentInfo.getId().toString());
+ emailParamsDTO.setCreateDate(String.valueOf(paymentInfo.getCreateTime()).replace("T", " "));
+ emailParamsDTO.setQuantity(String.valueOf(1));
+ emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
+ emailParamsDTO.setLastOrderDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setEndOfPrepaidTerm(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setPaymentMethod(subscriptionInfo.getPaymentMethod());
+ emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
+ emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
+ emailParamsDTO.setStartDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+
+ SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail());
+
+ // 邮件通知成功后,更新标志
+ PaymentInfo payment = new PaymentInfo();
+ payment.setId(paymentInfo.getId());
+ payment.setNotified(1);
+ payment.setUpdateTime(LocalDateTime.now());
+ paymentInfoMapper.updateById(payment);
+ return true;
}
- public String capturePaymentIntent(String clientSecret) throws StripeException {
+ 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);
- Stripe.apiKey = privateKey;
+ // 转为时间戳
+ long startTimestamp = startOfDay.toEpochSecond(ZoneOffset.UTC);
+ long endTimestamp = endOfDay.toEpochSecond(ZoneOffset.UTC);
- PaymentIntent resource = PaymentIntent.retrieve(clientSecret);
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.ge("current_period_end", startTimestamp);
+ qw.lt("current_period_end", endTimestamp);
+ qw.eq("status", "active");
- PaymentIntentCaptureParams params = PaymentIntentCaptureParams.builder().build();
-
- PaymentIntent paymentIntent = resource.capture(params);
- return null;
+ List subscriptionInfos = subscriptionInfoMapper.selectList(qw);
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+ boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), null);
+ if (b) log.info("提前7天向用户 {} 发送续订通知邮件", subscriptionInfo.getAccountId());
+ }
}
+ public void checkSubscriptionExpiration(){
+ long epochSecond = Instant.now().getEpochSecond();
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.lt("current_period_end", epochSecond);
+ qw.eq("status", "active");
+ List subscriptionInfos = subscriptionInfoMapper.selectList(qw);
+
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+ subscriptionInfo.setStatus("expired");
+ subscriptionInfo.setUpdateTime(LocalDateTime.now());
+ subscriptionInfoMapper.updateById(subscriptionInfo);
+ log.info("用户 {} 的订阅 {} 已过期", subscriptionInfo.getAccountId(), subscriptionInfo.getOrderNo());
+ }
+ }
}
diff --git a/src/main/resources/payment.properties b/src/main/resources/payment.properties
index 56e5a315..43c4e4b7 100644
--- a/src/main/resources/payment.properties
+++ b/src/main/resources/payment.properties
@@ -27,13 +27,13 @@ paypal.webhook_id=1D107312EX592781K
##### Stripe
# developer
-#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
-#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
+stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2
+stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w
# kim - test
#stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0
#stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u
# kim - live
-stripe.private-key=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
-stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11
\ No newline at end of file
+#stripe.private-key=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
+#stripe.webhook-sign-secret=whsec_hhGDgdelQRHSg4LmChtQe41crj41eb11
\ No newline at end of file
From f6f759110fddc981ad402b433c577912a85b0f24 Mon Sep 17 00:00:00 2001
From: xupei
Date: Tue, 19 Nov 2024 16:00:30 +0800
Subject: [PATCH 04/91] =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=BC=98=E5=8C=96--?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=A7=AF=E5=88=86=E8=B4=AD=E4=B9=B0=E7=9B=B8?=
=?UTF-8?q?=E5=BA=94=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/ai/da/service/CreditsService.java | 4 +-
.../ai/da/service/impl/AliPayServiceImpl.java | 8 ++-
.../da/service/impl/AlipayHKServiceImpl.java | 3 +-
.../da/service/impl/CreditsServiceImpl.java | 7 +-
.../da/service/impl/GenerateServiceImpl.java | 4 +-
.../impl/PayPalCheckoutServiceImpl.java | 28 ++++----
.../service/impl/PaymentInfoServiceImpl.java | 11 +++-
.../ai/da/service/impl/StripeServiceImpl.java | 64 ++++++++++++-------
.../impl/SuperResolutionServiceImpl.java | 2 +-
src/main/resources/messages_en.properties | 1 +
src/main/resources/messages_zh.properties | 1 +
11 files changed, 81 insertions(+), 52 deletions(-)
diff --git a/src/main/java/com/ai/da/service/CreditsService.java b/src/main/java/com/ai/da/service/CreditsService.java
index 1d076527..62d9e5e4 100644
--- a/src/main/java/com/ai/da/service/CreditsService.java
+++ b/src/main/java/com/ai/da/service/CreditsService.java
@@ -17,9 +17,9 @@ public interface CreditsService extends IService {
String getCredits(Long accountId);
- void creditsRefund(Long accountId, Integer quantity);
+ void creditsRefund(Long accountId, Integer quantity, String orderNo);
- void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType);
+ void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType, String orderNo);
PageBaseResponse queryCreditsDetailsPage(QueryIncomeOrExpenditureDTO queryPageByTimeDTO);
diff --git a/src/main/java/com/ai/da/service/impl/AliPayServiceImpl.java b/src/main/java/com/ai/da/service/impl/AliPayServiceImpl.java
index ccbd47d2..04fc8993 100644
--- a/src/main/java/com/ai/da/service/impl/AliPayServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AliPayServiceImpl.java
@@ -217,7 +217,8 @@ public class AliPayServiceImpl implements AliPayService {
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
+ "positive",
+ orderByOrderNo.getOrderNo());
} finally {
//要主动释放锁
lock.unlock();
@@ -320,7 +321,8 @@ public class AliPayServiceImpl implements AliPayService {
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--Alipay",
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
+ "positive",
+ orderByOrderNo.getOrderNo());
}
}
@@ -393,7 +395,7 @@ public class AliPayServiceImpl implements AliPayService {
// 更新积分状态
OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
// creditsService.creditsRefund(orderByOrderNo.getAccountId(), orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
- creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())));
+ creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())), orderNo);
} else {
log.info("调用失败,返回码 ===> " + response.getCode() + ", 返回描述 ===> " + response.getMsg());
diff --git a/src/main/java/com/ai/da/service/impl/AlipayHKServiceImpl.java b/src/main/java/com/ai/da/service/impl/AlipayHKServiceImpl.java
index f20d7350..417a03bd 100644
--- a/src/main/java/com/ai/da/service/impl/AlipayHKServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AlipayHKServiceImpl.java
@@ -248,7 +248,8 @@ public class AlipayHKServiceImpl implements AlipayHKService {
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--AlipayHK",
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
+ "positive",
+ orderByOrderNo.getOrderNo());
log.info("用户:{} 积分信息更新成功",orderByOrderNo.getAccountId());
} finally {
//要主动释放锁
diff --git a/src/main/java/com/ai/da/service/impl/CreditsServiceImpl.java b/src/main/java/com/ai/da/service/impl/CreditsServiceImpl.java
index 9cc17ce8..5c24211c 100644
--- a/src/main/java/com/ai/da/service/impl/CreditsServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/CreditsServiceImpl.java
@@ -100,7 +100,7 @@ public class CreditsServiceImpl extends ServiceImpl增 negative->减
*/
@Override
- public void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType) {
+ public void insertToCreditsDetail(Long accountId, String changeEvent, String credits, String changeType, String orderNo) {
CreditsDetail creditsDetail = new CreditsDetail();
Account account = accountMapper.selectById(accountId);
// BigDecimal finalCredits;
@@ -137,6 +137,7 @@ public class CreditsServiceImpl extends ServiceImpl i
if (b) creditsService.insertToCreditsDetail(accountId,
CreditsEventsEnum.TO_PRODUCT_IMAGE.getName(),
CreditsEventsEnum.TO_PRODUCT_IMAGE.getValue(),
- "negative");
+ "negative", null);
}
}
@@ -736,7 +736,7 @@ public class GenerateServiceImpl extends ServiceImpl i
if (b) creditsService.insertToCreditsDetail(accountId,
CreditsEventsEnum.RELIGHT.getName(),
CreditsEventsEnum.RELIGHT.getValue(),
- "negative");
+ "negative", null);
}
}
diff --git a/src/main/java/com/ai/da/service/impl/PayPalCheckoutServiceImpl.java b/src/main/java/com/ai/da/service/impl/PayPalCheckoutServiceImpl.java
index 22c57621..fb5d5409 100644
--- a/src/main/java/com/ai/da/service/impl/PayPalCheckoutServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PayPalCheckoutServiceImpl.java
@@ -441,11 +441,11 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
* 申请退款
*/
@Transactional(rollbackFor = Exception.class)
- public Boolean refundOrder(String orderId, String reason) throws IOException {
+ public Boolean refundOrder(String orderNo, String reason) throws IOException {
- RefundInfo refundByOrderNo = refundsInfoService.createRefundByOrderNo(orderId, reason);
+ RefundInfo refundByOrderNo = refundsInfoService.createRefundByOrderNo(orderNo, reason);
- OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderId);
+ OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderNo);
PayPalClient payPalClient = new PayPalClient();
HttpResponse ordersGetResponse = null;
ordersGetRequest.authorization("Bearer " + getOAuth());
@@ -461,7 +461,7 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
request.authorization("Bearer " + getOAuth());
request.prefer("return=representation");
- OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
+ OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
request.requestBody(buildRefundRequestBody(String.valueOf(orderInfo.getTotalFee()), reason));
HttpResponse response = null;
try {
@@ -476,7 +476,7 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
//进行数据库操作,修改状态为已退款(配合回调和退款查询确定退款成功)
//更新订单状态
- orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_SUCCESS);
+ orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_SUCCESS);
refundsInfoService.updateRefundForPayPal(
refundByOrderNo.getId(),
@@ -485,14 +485,14 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
AliPayTradeStateEnum.REFUND_SUCCESS.getType()); //退款成功
// 更新积分状态
- OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderId);
+ OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
// creditsService.creditsRefund(orderByOrderNo.getAccountId(), orderByOrderNo.getTotalFee() / Integer.parseInt(CreditsEventsEnum.PRICE.getValue()));
- creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())));
+ creditsService.creditsRefund(orderByOrderNo.getAccountId(), (int)(orderByOrderNo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue())), orderNo);
log.info("退款成功");
result = Boolean.TRUE;
} else {
//更新订单状态
- orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.REFUND_ABNORMAL);
+ orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.REFUND_ABNORMAL);
//更新退款单
refundsInfoService.updateRefundForPayPal(
@@ -571,19 +571,19 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
// 处理当前订单
@Transactional(rollbackFor = Exception.class)
- public void processOrder(String orderId) {
+ public void processOrder(String orderNo) {
// 1、确定当前订单是否已经被扣款
- OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderId);
+ OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
if (orderInfo.getOrderStatus().equals(OrderStatusEnum.SUCCESS.getType())) {
// 直接返回
return;
}
// 发起扣款请求
- Order capturedOrder = captureOrder(orderId);
+ Order capturedOrder = captureOrder(orderNo);
// 业务处理
if (PayPalOrderStatusEnum.COMPLETED.getStatus().equals(capturedOrder.status())) {
//更新订单状态
- orderInfoService.updateStatusByOrderNo(orderId, OrderStatusEnum.SUCCESS);
+ orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
//记录支付日志
paymentInfoService.createPaymentInfoForPayPal(capturedOrder);
float quantity = orderInfo.getTotalFee() / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
@@ -593,7 +593,7 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
creditsService.insertToCreditsDetail(orderInfo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--PayPal",
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
+ "positive", orderNo);
}
}
@@ -637,7 +637,7 @@ public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
CreditsEventsEnum.BUY_CREDITS.getName() + "--Paypal",
String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
- "positive");
+ "positive", orderNo);
}
}
diff --git a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
index 035e1135..36acae75 100644
--- a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
@@ -187,10 +187,15 @@ public class PaymentInfoServiceImpl extends ServiceImpl resp = new HashMap<>();
+ resp.put("paymentMethod", null);
+ resp.put("last4", null);
+ return resp;
} catch (StripeException e) {
throw new RuntimeException(e);
}
@@ -668,15 +684,15 @@ public class StripeServiceImpl implements StripeService {
return false;
}
PaymentInfo paymentInfo = paymentInfos.get(0);
- if (paymentInfo.getNotified() == 1){
- // 已经邮件通知过,直接返回
- return true;
- }
if (StringUtil.isNullOrEmpty(type)){
// 如果没有传入type,则使用paymentInfo中记录的类型
// (其实这里也可以通过invoiceId查询stripe,但是记录在自己的db中可以不用每次都查,且方便查看)
type = paymentInfo.getType();
}
+ if (!type.equals("reminder") && paymentInfo.getNotified() == 1){
+ // 已经邮件通知过,直接返回
+ return true;
+ }
com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(subscriptionInfo.getAccountId());
String userName = account.getUserName();
@@ -692,18 +708,20 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setPaymentMethod(subscriptionInfo.getPaymentMethod());
emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
- emailParamsDTO.setStartDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setStartDate(changeTimeStampFormat(subscriptionInfo.getCreateTime()));
emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail());
// 邮件通知成功后,更新标志
- PaymentInfo payment = new PaymentInfo();
- payment.setId(paymentInfo.getId());
- payment.setNotified(1);
- payment.setUpdateTime(LocalDateTime.now());
- paymentInfoMapper.updateById(payment);
+ if (!type.equals("reminder")){
+ PaymentInfo payment = new PaymentInfo();
+ payment.setId(paymentInfo.getId());
+ payment.setNotified(1);
+ payment.setUpdateTime(LocalDateTime.now());
+ paymentInfoMapper.updateById(payment);
+ }
return true;
}
@@ -723,7 +741,7 @@ public class StripeServiceImpl implements StripeService {
List subscriptionInfos = subscriptionInfoMapper.selectList(qw);
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
- boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), null);
+ boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), "reminder");
if (b) log.info("提前7天向用户 {} 发送续订通知邮件", subscriptionInfo.getAccountId());
}
}
diff --git a/src/main/java/com/ai/da/service/impl/SuperResolutionServiceImpl.java b/src/main/java/com/ai/da/service/impl/SuperResolutionServiceImpl.java
index 03ed3d69..eda65b46 100644
--- a/src/main/java/com/ai/da/service/impl/SuperResolutionServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/SuperResolutionServiceImpl.java
@@ -149,7 +149,7 @@ public class SuperResolutionServiceImpl extends ServiceImpl
Date: Tue, 19 Nov 2024 17:08:16 +0800
Subject: [PATCH 05/91] =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/ai/da/common/utils/SendEmailUtil.java | 20 +++++++++++--------
.../com/ai/da/service/PaymentInfoService.java | 2 +-
.../service/impl/PaymentInfoServiceImpl.java | 5 ++---
.../ai/da/service/impl/StripeServiceImpl.java | 14 +++++++------
4 files changed, 23 insertions(+), 18 deletions(-)
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 d1a30ae1..c52d3d8c 100644
--- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
+++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
@@ -782,7 +782,8 @@ public class SendEmailUtil {
public static void subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress){
try{
- String kimEmail = "kimwong@code-create.com.hk";
+// String merchantEmail = "kimwong@code-create.com.hk";
+ String merchantEmail = "xupei3360@163.com";
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
@@ -798,7 +799,7 @@ public class SendEmailUtil {
user.setDestination(new String[]{receiverAddress});
SendEmailRequest merchant = new SendEmailRequest();
merchant.setFromEmailAddress(SEND_ADDRESS);
- merchant.setDestination(new String[]{kimEmail});
+ merchant.setDestination(new String[]{merchantEmail});
Template templateUser = new Template();
Template templateMerchant = new Template();
switch (type) {
@@ -848,12 +849,15 @@ public class SendEmailUtil {
templateMerchant.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO));
merchant.setTemplate(templateMerchant);
- // 返回的resp是一个SendEmailResponse的实例,与请求对象对应
- SendEmailResponse respUser = client.SendEmail(user);
- // todo 暂时先不发商家邮件
-// SendEmailResponse respMerchant = client.SendEmail(merchant);
- log.info("邮件发送结果toUser###{}", SendEmailResponse.toJsonString(respUser));
-// log.info("邮件发送结果toMerchant###{}", SendEmailResponse.toJsonString(respMerchant));
+ if (!type.equals("cancel")){
+ // 返回的resp是一个SendEmailResponse的实例,与请求对象对应
+ SendEmailResponse respUser = client.SendEmail(user);
+ log.info("邮件发送结果toUser###{}", SendEmailResponse.toJsonString(respUser));
+ }
+ if (!type.equals("reminder")){
+ SendEmailResponse respMerchant = client.SendEmail(merchant);
+ log.info("邮件发送结果toMerchant###{}", SendEmailResponse.toJsonString(respMerchant));
+ }
} catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString());
throw new BusinessException("failed.to.send.mail");
diff --git a/src/main/java/com/ai/da/service/PaymentInfoService.java b/src/main/java/com/ai/da/service/PaymentInfoService.java
index 6d3d2d5e..fcc69d00 100644
--- a/src/main/java/com/ai/da/service/PaymentInfoService.java
+++ b/src/main/java/com/ai/da/service/PaymentInfoService.java
@@ -18,7 +18,7 @@ public interface PaymentInfoService extends IService {
void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO);
- String createPaymentInfoForStripe(Invoice invoice);
+ PaymentInfo createPaymentInfoForStripe(Invoice invoice);
PaymentInfo getPaymentInfoByOrderId(String orderId);
diff --git a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
index 36acae75..e78fd856 100644
--- a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
@@ -176,7 +176,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl resp = new HashMap<>();
- resp.put("paymentMethod", null);
- resp.put("last4", null);
+ resp.put("paymentMethod", "N/A");
+ resp.put("last4", "N/A");
return resp;
} catch (StripeException e) {
throw new RuntimeException(e);
@@ -697,6 +698,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();
+ OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(subscriptionInfo.getOrderNo());
emailParamsDTO.setUsername(userName);
emailParamsDTO.setOrderId(paymentInfo.getId().toString());
@@ -708,7 +710,7 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setPaymentMethod(subscriptionInfo.getPaymentMethod());
emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
- emailParamsDTO.setStartDate(changeTimeStampFormat(subscriptionInfo.getCreateTime()));
+ emailParamsDTO.setStartDate(changeTimeStampFormat(orderByOrderNo.getCreateTime()));
emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
From 5019fbd3fc6156227c97f683e627f89a607be7d7 Mon Sep 17 00:00:00 2001
From: xupei
Date: Mon, 25 Nov 2024 10:53:09 +0800
Subject: [PATCH 06/91] =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ai/da/common/enums/OrderStatusEnum.java | 5 +
.../com/ai/da/common/utils/SendEmailUtil.java | 16 +-
.../da/mapper/primary/entity/OrderInfo.java | 2 +
.../da/mapper/primary/entity/PaymentInfo.java | 4 +
.../primary/entity/SubscriptionInfo.java | 9 +-
.../model/dto/SubscriptionEmailParamsDTO.java | 5 +
.../com/ai/da/service/PaymentInfoService.java | 5 +-
.../service/impl/PaymentInfoServiceImpl.java | 95 ++++++++++-
.../ai/da/service/impl/StripeServiceImpl.java | 151 +++++++++++++++---
9 files changed, 257 insertions(+), 35 deletions(-)
diff --git a/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java b/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
index aac9719d..8f8699a6 100644
--- a/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
+++ b/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
@@ -17,6 +17,11 @@ public enum OrderStatusEnum {
*/
SUCCESS("支付成功"),
+ /**
+ * 支付失败
+ */
+ FAILURE("支付失败"),
+
/**
* 已关闭
*/
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 c52d3d8c..c6d61e68 100644
--- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
+++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
@@ -779,6 +779,8 @@ public class SendEmailUtil {
private final static Long RENEWAL_USER_CN = 130726L;
private final static Long RENEWAL_REMINDER_USER_EN = 130727L;
private final static Long RENEWAL_REMINDER_USER_CN = 130728L;
+ private final static Long PAYMENT_FAILED_NEW_MERCHANT_EN = 131230L;
+ private final static Long PAYMENT_FAILED_RENEWAL_MERCHANT_EN = 131225L;
public static void subscriptionEmailReminder(String type, SubscriptionEmailParamsDTO subscriptionEmailParamsDTO, String language, String receiverAddress){
try{
@@ -807,6 +809,14 @@ public class SendEmailUtil {
merchant.setSubject("[Code-Create] Subscription Cancelled");
templateMerchant.setTemplateID(CANCEL_MERCHANT_EN);
break;
+ case "fail_new":
+ merchant.setSubject("[Code-Create] Payment Failed : New Order (" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateMerchant.setTemplateID(PAYMENT_FAILED_NEW_MERCHANT_EN);
+ break;
+ case "fail_renewal":
+ merchant.setSubject("[Code-Create] Payment Failed : Renewal Order (" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateMerchant.setTemplateID(PAYMENT_FAILED_RENEWAL_MERCHANT_EN);
+ break;
case "new":
merchant.setSubject("[Code-Create] New Order(" + subscriptionEmailParamsDTO.getOrderId() + ")");
templateMerchant.setTemplateID(NEW_MERCHANT_EN);
@@ -849,14 +859,14 @@ public class SendEmailUtil {
templateMerchant.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO));
merchant.setTemplate(templateMerchant);
- if (!type.equals("cancel")){
+ if (!type.equals("cancel") && !type.equals("fail_new") && !type.equals("fail_renewal") ){
// 返回的resp是一个SendEmailResponse的实例,与请求对象对应
SendEmailResponse respUser = client.SendEmail(user);
- log.info("邮件发送结果toUser###{}", SendEmailResponse.toJsonString(respUser));
+ log.info("邮件主题:{},发送结果toUser###{}", user.getSubject(), SendEmailResponse.toJsonString(respUser));
}
if (!type.equals("reminder")){
SendEmailResponse respMerchant = client.SendEmail(merchant);
- log.info("邮件发送结果toMerchant###{}", SendEmailResponse.toJsonString(respMerchant));
+ log.info("邮件主题:{},发送结果toMerchant###{}", merchant.getSubject(), SendEmailResponse.toJsonString(respMerchant));
}
} catch (TencentCloudSDKException e) {
log.info("邮件发送失败###{}", e.toString());
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 98a020b9..e5bc0a98 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
@@ -21,5 +21,7 @@ public class OrderInfo extends BaseEntity{
private String orderStatus;//订单状态
+ private String note;
+
private String paymentType;//支付方式
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
index 3806d888..8172f4bd 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
@@ -26,4 +26,8 @@ public class PaymentInfo extends BaseEntity{
// 当前支付是否已邮件通知 0 || 1
private Integer notified;
+
+ private String paymentMethod;
+
+ private String last4;
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
index 2b61cf48..b7895c08 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/SubscriptionInfo.java
@@ -22,14 +22,7 @@ public class SubscriptionInfo extends BaseEntity{
// active || expired
private String status = "active";
- // 是否自动续订
- private byte autoRenewal = (byte)1;
-
- // 支付方式
- private String paymentMethod;
-
- // 如果是用卡支付,可以看到银行卡最后四位
- private String last4;
+ private byte cancelNotified = (byte)0;
// 续订的下一个付款日
private String nextPayDate;
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 0020582f..9ecf0241 100644
--- a/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
+++ b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
@@ -28,6 +28,8 @@ public class SubscriptionEmailParamsDTO {
// 付款方式
private String paymentMethod;
+ private String last4;
+
// 订阅Id
private String subscriptionId;
@@ -43,5 +45,8 @@ public class SubscriptionEmailParamsDTO {
// 下次付款时间(reminder)
private String renewalTime;
+ // 付款失败原因
+ private String failMessage;
+
}
diff --git a/src/main/java/com/ai/da/service/PaymentInfoService.java b/src/main/java/com/ai/da/service/PaymentInfoService.java
index fcc69d00..785fb286 100644
--- a/src/main/java/com/ai/da/service/PaymentInfoService.java
+++ b/src/main/java/com/ai/da/service/PaymentInfoService.java
@@ -4,6 +4,7 @@ import com.ai.da.mapper.primary.entity.PaymentInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import com.paypal.orders.Order;
+import com.stripe.model.Charge;
import com.stripe.model.Invoice;
import java.util.Map;
@@ -18,7 +19,9 @@ public interface PaymentInfoService extends IService {
void createPaymentInfoForAliPayHK(AlipayHKCallbackDTO alipayHKCallbackDTO);
- PaymentInfo createPaymentInfoForStripe(Invoice invoice);
+ PaymentInfo createOrUpdatePaymentInfoForStripe(Invoice invoice);
+
+ PaymentInfo createOrUpdatePaymentInfoForStripe(Charge charge);
PaymentInfo getPaymentInfoByOrderId(String orderId);
diff --git a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
index e78fd856..204a9ce4 100644
--- a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
@@ -11,6 +11,7 @@ import com.google.gson.Gson;
import com.paypal.orders.Order;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
+import com.stripe.model.Charge;
import com.stripe.model.Invoice;
import com.stripe.model.Subscription;
import com.stripe.model.checkout.Session;
@@ -20,6 +21,7 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -28,6 +30,12 @@ import java.util.Objects;
@Slf4j
public class PaymentInfoServiceImpl extends ServiceImpl implements PaymentInfoService {
+ private final StripeServiceImpl stripeServiceImpl;
+
+ public PaymentInfoServiceImpl(StripeServiceImpl stripeServiceImpl) {
+ this.stripeServiceImpl = stripeServiceImpl;
+ }
+
/**
* 记录支付日志:微信支付
* @param plainText
@@ -152,7 +160,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl qw = new QueryWrapper<>();
qw.eq("transaction_id", invoiceId);
PaymentInfo paymentInfo = baseMapper.selectOne(qw);
+ String status = invoice.getStatus();
// 判断当前支付是否已经被记录,确保同一个支付不会被重复记录
if (Objects.isNull(paymentInfo)){
String orderNo;
@@ -199,13 +208,19 @@ public class PaymentInfoServiceImpl extends ServiceImpl paymentMethod = stripeServiceImpl.getPaymentMethodByInvoiceId(invoiceId);
paymentInfo = new PaymentInfo();
paymentInfo.setOrderNo(orderNo);
@@ -219,12 +234,84 @@ public class PaymentInfoServiceImpl extends ServiceImpl qw = new QueryWrapper<>();
+ qw.eq("transaction_id", charge.getInvoice());
+ PaymentInfo paymentInfo = baseMapper.selectOne(qw);
+ Charge.PaymentMethodDetails paymentMethodDetails = charge.getPaymentMethodDetails();
+ String paymentMethod;
+ String last4 = "N/A";
+ switch (paymentMethodDetails.getType()){
+ case "alipay":
+ paymentMethod = "Alipay";
+ break;
+ case "bancontact":
+ paymentMethod = "BanContact";
+ break;
+ case "card":
+ Charge.PaymentMethodDetails.Card card = paymentMethodDetails.getCard();
+ String brand = card.getBrand();
+ brand = brand.substring(0, 1).toUpperCase() + brand.substring(1);
+ paymentMethod = brand + " " + card.getFunding() + "card";
+ last4 = card.getLast4();
+ break;
+ case "eps":
+ Charge.PaymentMethodDetails.Eps eps = paymentMethodDetails.getEps();
+ paymentMethod = eps.getBank();
+ break;
+ case "giropay":
+ paymentMethod = "GiroPay";
+ break;
+ case "ideal":
+ Charge.PaymentMethodDetails.Ideal ideal = paymentMethodDetails.getIdeal();
+ paymentMethod = ideal.getBank();
+ break;
+ case "link":
+ paymentMethod = "Link";
+ break;
+ default:
+ paymentMethod = "N/A";
+ }
+ if (Objects.isNull(paymentInfo)){
+ Stripe.apiKey = privateKey;
+
+ Float divide = new BigDecimal(charge.getAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).floatValue();
+ paymentInfo = new PaymentInfo();
+ paymentInfo.setOrderNo(charge.getDescription().replace("AiDA - ", ""));
+ paymentInfo.setTransactionId(charge.getInvoice());
+ paymentInfo.setPaymentType(PayTypeEnum.STRIPE.getType());
+ paymentInfo.setTradeState(charge.getStatus());
+ paymentInfo.setPayerTotal(divide);
+ paymentInfo.setNotified(0);
+ paymentInfo.setPaymentMethod(paymentMethod);
+ paymentInfo.setLast4(last4);
+ paymentInfo.setCreateTime(LocalDateTime.now());
+ baseMapper.insert(paymentInfo);
+ }else {
+ paymentInfo.setTradeState(charge.getStatus());
+ paymentInfo.setPaymentMethod(paymentMethod);
+ paymentInfo.setLast4(last4);
+ paymentInfo.setUpdateTime(LocalDateTime.now());
+ baseMapper.updateById(paymentInfo);
+ }
+
+ return paymentInfo;
+ }
+
@Override
public PaymentInfo getPaymentInfoByOrderId(String orderId){
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 fe903590..dba20370 100644
--- a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java
@@ -271,6 +271,10 @@ public class StripeServiceImpl implements StripeService {
Session session = (Session) stripeObject;
if (event.getType().equals("checkout.session.completed")) {
processOrder(session);
+ }else if (event.getType().equals("checkout.session.expired")){
+ String orderNo = session.getMetadata().get("orderId");
+ // 会话过期 未支付 且之后没有支付成功的订单
+ processExpiredOrder(orderNo);
}
} else if (stripeObject instanceof Subscription){
Subscription subscription = (Subscription) stripeObject;
@@ -280,25 +284,30 @@ public class StripeServiceImpl implements StripeService {
log.info("创建连续订阅");
} else if (event.getType().equals("customer.subscription.updated")){
// 更新订阅信息
- response = updateSubscription(subscription);
+ updateSubscription(subscription);
log.info("订阅更新");
if (subscription.getStatus().equals("active")){
response = sendEmail(subscription.getId(), null);
}
} else if (event.getType().equals("customer.subscription.deleted")){
- response = updateSubscription(subscription);
+ SubscriptionInfo subscriptionInfo = updateSubscription(subscription);
log.info("用户取消连续订阅");
- } else if (event.getType().equals("customer.subscription.paused")){
+ if (subscriptionInfo.getCancelNotified() == (byte)0){
+ response = sendEmail(subscription.getId(), "cancel");
+ }
+ subscriptionInfo.setCancelNotified((byte)1);
+ subscriptionInfoMapper.updateById(subscriptionInfo);
+ }/* else if (event.getType().equals("customer.subscription.paused")){
updateSubscription(subscription);
} else if (event.getType().equals("customer.subscription.resumed")){
updateSubscription(subscription);
log.info("用户订阅恢复");
- }
+ }*/
} else if (stripeObject instanceof Invoice) {
Invoice invoice = (Invoice) stripeObject;
if (event.getType().equals("invoice.paid")) {
// 新增支付成功的信息,返回orderNo,表示,该回调第一次被记录
- PaymentInfo paymentInfo = paymentInfoService.createPaymentInfoForStripe(invoice);
+ PaymentInfo paymentInfo = paymentInfoService.createOrUpdatePaymentInfoForStripe(invoice);
// 当前支付没有被通知时才需要发送通知邮件
if (paymentInfo.getNotified().equals(0)) {
@@ -311,6 +320,43 @@ public class StripeServiceImpl implements StripeService {
response = sendEmail(invoice.getSubscription(), "renewal");
}
}
+ } else if (event.getType().equals("invoice.payment_failed")) {
+ // 更新支付信息
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("transaction_id", invoice.getId());
+ PaymentInfo paymentInfo = paymentInfoService.getBaseMapper().selectOne(queryWrapper);
+ if (!Objects.isNull(paymentInfo)){
+ String type = invoice.getBillingReason().equals("subscription_create") ? "new" :
+ invoice.getBillingReason().equals("subscription_cycle") ? "renewal" : invoice.getBillingReason();
+ Gson gson = new Gson();
+ String json = gson.toJson(invoice);
+ paymentInfo.setContent(json);
+ paymentInfo.setType(type);
+ paymentInfoService.updateById(paymentInfo);
+ }
+ /*// 支付失败 通知商家的条件 1、会话过期 2、支付失败 3、这个用户在这个支付失败后再无支付成功的订阅
+ if (invoice.getBillingReason().equals("subscription_create")){
+ response = sendEmail(paymentInfo.getOrderNo());
+ } else if (invoice.getBillingReason().equals("subscription_cycle")){
+ response = sendEmail(invoice.getSubscription(), "fail_renewal");
+ }*/
+ }
+ }else if (stripeObject instanceof Charge) {
+ Charge charge = (Charge) stripeObject;
+ String orderNo = charge.getDescription().replace("AiDA - ", "");
+ OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
+ if (event.getType().equals("charge.failed")){
+ // 添加支付信息 && 更新支付信息
+ // 支付失败时,无法通过invoice_id获取支付方式,所以使用charge.failed回调添加支付信息
+ paymentInfoService.createOrUpdatePaymentInfoForStripe(charge);
+
+ orderInfo.setOrderStatus(OrderStatusEnum.FAILURE.getType());
+ orderInfo.setNote(charge.getFailureMessage());
+ orderInfoService.updateById(orderInfo);
+ }else if (event.getType().equals("charge.succeeded")){
+ orderInfo.setOrderStatus(OrderStatusEnum.SUCCESS.getType());
+ orderInfo.setNote("");
+ orderInfoService.updateById(orderInfo);
}
}
return response;
@@ -351,6 +397,36 @@ public class StripeServiceImpl implements StripeService {
}
}
+ private void processExpiredOrder(String orderNo) {
+ // 支付失败 通知商家的条件 1、会话过期 2、支付失败 3、这个用户在这个支付失败后再无支付成功的订阅
+ // 1、获取当前订单的支付状态
+// String orderNo = session.getMetadata().get("orderId");
+ OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
+ // 2、确认订单状态为支付失败
+ if (orderByOrderNo.getOrderStatus().equals(OrderStatusEnum.FAILURE.getType())) {
+ // 3、判断失败订单之后再无成功的订单
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("account_id", orderByOrderNo.getAccountId());
+ queryWrapper.gt("create_time", orderByOrderNo.getCreateTime());
+ queryWrapper.eq("order_status", OrderStatusEnum.SUCCESS.getType());
+ queryWrapper.likeLeft("title", "Subscription");
+ List orderInfos = orderInfoService.getBaseMapper().selectList(queryWrapper);
+ if (orderInfos.isEmpty()) {
+ // 4、判断当前订单有没有订阅信息
+ QueryWrapper qw = new QueryWrapper<>();
+ qw.eq("order_no", orderNo);
+ SubscriptionInfo subscriptionInfo = subscriptionInfoMapper.selectOne(qw);
+ // 发送邮件通知商家用户支付失败
+ if (Objects.isNull(subscriptionInfo) || subscriptionInfo.getStatus().equals("incomplete")) {
+ sendEmail(orderNo);
+ }else {
+ sendEmail(subscriptionInfo.getSubscriptionId(), "fail_renewal");
+ }
+ }
+ }
+
+ }
+
@Transactional(rollbackFor = Exception.class)
public SubscriptionInfo createSubscription(Subscription subscription){
// 确认当前subscription是否已经记录
@@ -366,7 +442,6 @@ public class StripeServiceImpl implements StripeService {
// 从回调信息中获取recurring type
SubscriptionItem subscriptionItem = subscription.getItems().getData().get(0);
String interval = subscriptionItem.getPrice().getRecurring().getInterval();
- Map paymentMethod = getPaymentMethodByInvoiceId(subscription.getLatestInvoice());
subscriptionInfo = new SubscriptionInfo();
subscriptionInfo.setAccountId(orderInfo.getAccountId());
@@ -374,8 +449,6 @@ public class StripeServiceImpl implements StripeService {
subscriptionInfo.setSubscriptionId(subscription.getId());
subscriptionInfo.setType(interval);
subscriptionInfo.setStatus(subscription.getStatus());
- subscriptionInfo.setPaymentMethod(paymentMethod.get("paymentMethod"));
- subscriptionInfo.setLast4(paymentMethod.get("last4"));
subscriptionInfo.setNextPayDate(changeTimeStampFormat(subscription.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE));
subscriptionInfo.setCurrentPeriodStart(subscription.getCurrentPeriodStart());
subscriptionInfo.setCurrentPeriodEnd(subscription.getCurrentPeriodEnd());
@@ -406,7 +479,7 @@ public class StripeServiceImpl implements StripeService {
}
@Transactional(rollbackFor = Exception.class)
- public Boolean updateSubscription(Subscription subscription){
+ public SubscriptionInfo updateSubscription(Subscription subscription){
// 获取当前是否有已经记录的subscriptionInfo
SubscriptionInfo subscriptionInfo = createSubscription(subscription);
// 用于标志数据有没有变动,避免在没有改动的情况下频繁的更新数据库
@@ -415,9 +488,6 @@ public class StripeServiceImpl implements StripeService {
subscriptionInfo.setStatus(subscription.getStatus());
flag = true;
}
- if (subscription.getStatus().equals("canceled")){
- subscriptionInfo.setAutoRenewal((byte) 0);
- }
if (!subscriptionInfo.getCurrentPeriodStart().equals(subscription.getCurrentPeriodStart())){
subscriptionInfo.setCurrentPeriodStart(subscription.getCurrentPeriodStart());
flag = true;
@@ -432,10 +502,9 @@ public class StripeServiceImpl implements StripeService {
}
if (flag){
subscriptionInfo.setUpdateTime(LocalDateTime.now());
- // todo 这里需要再检查支付方式吗?
subscriptionInfoMapper.updateById(subscriptionInfo);
}
- return true;
+ return subscriptionInfo;
}
private void updateAccountValidity(Long accountId, Long currentPeriodEnd){
@@ -633,6 +702,7 @@ public class StripeServiceImpl implements StripeService {
switch (retrieve.getType()){
case "alipay":
paymentMethod = "Alipay";
+ last4 = "N/A";
break;
case "bancontact":
paymentMethod = "BanContact";
@@ -647,17 +717,24 @@ public class StripeServiceImpl implements StripeService {
case "eps":
PaymentMethod.Eps eps = retrieve.getEps();
paymentMethod = eps.getBank();
+ last4 = "N/A";
break;
case "giropay":
paymentMethod = "GiroPay";
+ last4 = "N/A";
break;
case "ideal":
PaymentMethod.Ideal ideal = retrieve.getIdeal();
paymentMethod = ideal.getBank();
+ last4 = "N/A";
break;
case "link":
paymentMethod = "Link";
+ last4 = "N/A";
break;
+ default:
+ paymentMethod = "N/A";
+ last4 = "N/A";
}
HashMap resp = new HashMap<>();
resp.put("paymentMethod", paymentMethod);
@@ -669,7 +746,7 @@ public class StripeServiceImpl implements StripeService {
// return null;
}
- public boolean sendEmail(String subscriptionId, String type){
+ public boolean sendEmail(String subscriptionId, String type) {
SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO();
QueryWrapper qwSI = new QueryWrapper<>();
@@ -688,9 +765,9 @@ public class StripeServiceImpl implements StripeService {
if (StringUtil.isNullOrEmpty(type)){
// 如果没有传入type,则使用paymentInfo中记录的类型
// (其实这里也可以通过invoiceId查询stripe,但是记录在自己的db中可以不用每次都查,且方便查看)
- type = paymentInfo.getType();
+ type = StringUtil.isNullOrEmpty(paymentInfo.getType()) ? "new" : paymentInfo.getType();
}
- if (!type.equals("reminder") && paymentInfo.getNotified() == 1){
+ if (!type.equals("reminder") && !type.equals("cancel") && paymentInfo.getNotified() == 1){
// 已经邮件通知过,直接返回
return true;
}
@@ -707,8 +784,10 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
emailParamsDTO.setLastOrderDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setEndOfPrepaidTerm(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
- emailParamsDTO.setPaymentMethod(subscriptionInfo.getPaymentMethod());
+ emailParamsDTO.setPaymentMethod(paymentInfo.getPaymentMethod());
+ emailParamsDTO.setLast4(paymentInfo.getLast4());
emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
+ emailParamsDTO.setFailMessage(orderByOrderNo.getNote());
emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
emailParamsDTO.setStartDate(changeTimeStampFormat(orderByOrderNo.getCreateTime()));
emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
@@ -717,7 +796,7 @@ public class StripeServiceImpl implements StripeService {
SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail());
// 邮件通知成功后,更新标志
- if (!type.equals("reminder")){
+ if (!type.equals("reminder") && !type.equals("cancel")){
PaymentInfo payment = new PaymentInfo();
payment.setId(paymentInfo.getId());
payment.setNotified(1);
@@ -727,6 +806,40 @@ public class StripeServiceImpl implements StripeService {
return true;
}
+ public boolean sendEmail(String orderNo){
+ SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO();
+ OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
+
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(orderInfo.getAccountId());
+ String userName = account.getUserName();
+ String language = account.getLanguage();
+ QueryWrapper qwPI = new QueryWrapper<>();
+ qwPI.eq("order_no", orderNo);
+ List paymentInfos = paymentInfoMapper.selectList(qwPI);
+ if (paymentInfos.isEmpty()) {
+ return false;
+ }
+ PaymentInfo paymentInfo = paymentInfos.get(0);
+ emailParamsDTO.setUsername(userName);
+ emailParamsDTO.setOrderId(paymentInfo.getId().toString());
+ emailParamsDTO.setCreateDate(String.valueOf(paymentInfo.getCreateTime()).replace("T", " "));
+ emailParamsDTO.setQuantity(String.valueOf(1));
+ emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
+ emailParamsDTO.setFailMessage(orderInfo.getNote());
+ emailParamsDTO.setPaymentMethod(paymentInfo.getPaymentMethod());
+ emailParamsDTO.setLast4(paymentInfo.getLast4());
+
+ SendEmailUtil.subscriptionEmailReminder("fail_new", emailParamsDTO, language, account.getUserEmail());
+
+ // 邮件通知成功后,更新标志
+ PaymentInfo payment = new PaymentInfo();
+ payment.setId(paymentInfo.getId());
+ payment.setNotified(1);
+ payment.setUpdateTime(LocalDateTime.now());
+ paymentInfoMapper.updateById(payment);
+ return true;
+ }
+
public void subscriptionReminder(){
// 提前7天的 00:00:00 和 23:59:59
LocalDateTime startOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atStartOfDay();
From 1b15aed6a28801b2a6f8f433f01678d7d19ca35a Mon Sep 17 00:00:00 2001
From: xupei
Date: Thu, 28 Nov 2024 10:43:06 +0800
Subject: [PATCH 07/91] =?UTF-8?q?=E6=94=AF=E4=BB=98=E4=BC=98=E5=8C=96-?=
=?UTF-8?q?=E7=BB=AD=E8=AE=A2=E5=A4=B1=E8=B4=A5=E9=82=AE=E4=BB=B6=E9=80=9A?=
=?UTF-8?q?=E7=9F=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ai/da/common/enums/OrderStatusEnum.java | 9 -
.../com/ai/da/common/utils/SendEmailUtil.java | 12 +-
.../ai/da/controller/StripeController.java | 42 +++-
.../da/mapper/primary/entity/PaymentInfo.java | 3 +
.../ai/da/model/dto/ProductPurchaseDTO.java | 2 -
.../model/dto/SubscriptionEmailParamsDTO.java | 2 +
.../java/com/ai/da/service/StripeService.java | 10 +-
.../service/impl/PaymentInfoServiceImpl.java | 1 +
.../ai/da/service/impl/StripeServiceImpl.java | 230 ++++++++++++++++--
9 files changed, 269 insertions(+), 42 deletions(-)
diff --git a/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java b/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
index 8f8699a6..02767686 100644
--- a/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
+++ b/src/main/java/com/ai/da/common/enums/OrderStatusEnum.java
@@ -10,43 +10,34 @@ public enum OrderStatusEnum {
* 未支付
*/
NOT_PAY("未支付"),
-
-
/**
* 支付成功
*/
SUCCESS("支付成功"),
-
/**
* 支付失败
*/
FAILURE("支付失败"),
-
/**
* 已关闭
*/
TIMEOUT_CLOSED("超时已关闭"),
-
/**
* 已取消
*/
CANCEL("用户已取消"),
-
/**
* 退款中
*/
REFUND_PROCESSING("退款中"),
-
/**
* 已退款
*/
REFUND_SUCCESS("已退款"),
-
/**
* 退款异常
*/
REFUND_ABNORMAL("退款异常"),
-
/**
* paypal订单状态为 APPROVED
*/
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 c6d61e68..b14cd28a 100644
--- a/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
+++ b/src/main/java/com/ai/da/common/utils/SendEmailUtil.java
@@ -781,6 +781,8 @@ public class SendEmailUtil {
private final static Long RENEWAL_REMINDER_USER_CN = 130728L;
private final static Long PAYMENT_FAILED_NEW_MERCHANT_EN = 131230L;
private final static Long PAYMENT_FAILED_RENEWAL_MERCHANT_EN = 131225L;
+ 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{
@@ -816,6 +818,14 @@ public class SendEmailUtil {
case "fail_renewal":
merchant.setSubject("[Code-Create] Payment Failed : Renewal Order (" + subscriptionEmailParamsDTO.getOrderId() + ")");
templateMerchant.setTemplateID(PAYMENT_FAILED_RENEWAL_MERCHANT_EN);
+ // todo to user
+ if (language.equals("ENGLISH")){
+ user.setSubject("[Code-Create] Payment Failed : Renewal Order (" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateUser.setTemplateID(PAYMENT_FAILED_RENEWAL_USER_EN);
+ }else {
+ user.setSubject("[Code-Create] 自动续费失败 (" + subscriptionEmailParamsDTO.getOrderId() + ")");
+ templateUser.setTemplateID(PAYMENT_FAILED_RENEWAL_USER_CN);
+ }
break;
case "new":
merchant.setSubject("[Code-Create] New Order(" + subscriptionEmailParamsDTO.getOrderId() + ")");
@@ -859,7 +869,7 @@ public class SendEmailUtil {
templateMerchant.setTemplateData(JSON.toJSONString(subscriptionEmailParamsDTO));
merchant.setTemplate(templateMerchant);
- if (!type.equals("cancel") && !type.equals("fail_new") && !type.equals("fail_renewal") ){
+ if (!type.equals("cancel") && !type.equals("fail_new") ){
// 返回的resp是一个SendEmailResponse的实例,与请求对象对应
SendEmailResponse respUser = client.SendEmail(user);
log.info("邮件主题:{},发送结果toUser###{}", user.getSubject(), SendEmailResponse.toJsonString(respUser));
diff --git a/src/main/java/com/ai/da/controller/StripeController.java b/src/main/java/com/ai/da/controller/StripeController.java
index eac49851..8becf26b 100644
--- a/src/main/java/com/ai/da/controller/StripeController.java
+++ b/src/main/java/com/ai/da/controller/StripeController.java
@@ -9,19 +9,22 @@ import com.stripe.exception.StripeException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
-import org.simpleframework.xml.core.Validate;
import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
import java.io.IOException;
+import java.util.List;
@Api(tags = "Stripe模块")
@Slf4j
@RestController
@RequestMapping("/api/stripe")
+@ApiIgnore
public class StripeController {
@Resource
@@ -29,7 +32,7 @@ public class StripeController {
@ApiOperation("创建支付链接")
@PostMapping("/createOrder")
- public Response pay(@Validate @RequestBody ProductPurchaseDTO productPurchaseDTO) {
+ public Response pay(@Valid @RequestBody ProductPurchaseDTO productPurchaseDTO) {
return Response.success(stripeService.pay(productPurchaseDTO));
}
@@ -45,7 +48,7 @@ public class StripeController {
}
@ApiOperation("申请退款")
- @PostMapping("/trade/refund/{orderNo}/{reason}")
+ @GetMapping("/trade/refund/{orderNo}/{reason}")
public Response> refund(@PathVariable String orderNo, @PathVariable String reason) throws IOException {
String response = stripeService.refund(null,orderNo,reason);
if (response.equals("退款成功")){
@@ -56,19 +59,44 @@ public class StripeController {
}
@ApiOperation("获取订阅")
- @PostMapping("/getSubscription")
- public void getSubscription() {
+ @GetMapping("/getSubscription")
+ public Response> getSubscription(@RequestParam String name, @RequestParam String email) {
try {
- stripeService.getSubscription("xp", "xupei3360@163.com");
+ return Response.success(stripeService.getSubscriptionIds(name, email));
} catch (StripeException e) {
throw new RuntimeException(e);
}
}
@ApiOperation("取消订阅")
- @PostMapping("/cancelSubscription")
+ @GetMapping("/cancelSubscription")
public Response cancelSubscription(@RequestParam String subscriptionId) {
stripeService.cancelSubscription(subscriptionId);
return Response.success("success");
}
+ @ApiOperation("临时 取消订阅")
+ @GetMapping("/cancelSubscriptionTemp")
+ public Response cancelSubscriptionTemp(@RequestParam String subscriptionId) {
+ stripeService.cancelSubscriptionTemp(subscriptionId);
+ return Response.success("success");
+ }
+
+ @ApiOperation("创建订阅 临时")
+ @GetMapping("/createSubscriptionTemp")
+ public Response createSubscriptionTemp(@RequestParam String name, @RequestParam String email) {
+ return Response.success(stripeService.createSubscriptionTemp(name, email));
+ }
+
+ @ApiOperation("修改用户默认支付方式 临时")
+ @GetMapping("/changeCustomerPayment")
+ public Response changeCustomerPayment(@RequestParam String name, @RequestParam String email) {
+ return Response.success(stripeService.changeCustomerPayment(name, email));
+ }
+
+ @ApiOperation("临时 发送续订失败邮件")
+ @GetMapping("/sendRenewalFailEmail")
+ public Response sendRenewalFailEmail(@RequestParam String invoiceId, @RequestParam String subscriptionId, @RequestParam String orderNo) {
+ return Response.success(stripeService.sendRenewalFailEmail(invoiceId, subscriptionId,orderNo));
+ }
+
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
index 8172f4bd..d3533078 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/PaymentInfo.java
@@ -30,4 +30,7 @@ public class PaymentInfo extends BaseEntity{
private String paymentMethod;
private String last4;
+
+ // 发票托管页面
+ private String hostedInvoiceUrl;
}
diff --git a/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java b/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
index ce1fb595..0e386e8a 100644
--- a/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
+++ b/src/main/java/com/ai/da/model/dto/ProductPurchaseDTO.java
@@ -27,6 +27,4 @@ public class ProductPurchaseDTO {
@ApiModelProperty("是否自动续订 one_time || recurring")
private Boolean autoRenewal;
-
- private String refId;
}
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 9ecf0241..1f35b705 100644
--- a/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
+++ b/src/main/java/com/ai/da/model/dto/SubscriptionEmailParamsDTO.java
@@ -48,5 +48,7 @@ public class SubscriptionEmailParamsDTO {
// 付款失败原因
private String failMessage;
+ private String accountPageRef;
+
}
diff --git a/src/main/java/com/ai/da/service/StripeService.java b/src/main/java/com/ai/da/service/StripeService.java
index b517b325..268f08d2 100644
--- a/src/main/java/com/ai/da/service/StripeService.java
+++ b/src/main/java/com/ai/da/service/StripeService.java
@@ -2,7 +2,6 @@ package com.ai.da.service;
import com.ai.da.model.dto.ProductPurchaseDTO;
import com.stripe.exception.StripeException;
-import com.stripe.model.Subscription;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@@ -18,10 +17,12 @@ public interface StripeService {
void checkOrderStatus(String orderNo);
- List getSubscription(String name, String userEmail) throws StripeException;
+ List getSubscriptionIds(String name, String userEmail) throws StripeException;
void cancelSubscription(String orderNo);
+ void cancelSubscriptionTemp(String subscriptionId);
+
Map getPaymentMethod(String paymentMethodId);
/*void updateSubscription(String subscriptionId);
@@ -32,4 +33,9 @@ public interface StripeService {
void checkSubscriptionExpiration();
+ String createSubscriptionTemp(String name, String email);
+
+ String changeCustomerPayment(String name, String email);
+
+ boolean sendRenewalFailEmail(String invoiceId, String subscriptionId, String orderNo);
}
diff --git a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
index 204a9ce4..3459bf93 100644
--- a/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/PaymentInfoServiceImpl.java
@@ -236,6 +236,7 @@ public class PaymentInfoServiceImpl extends ServiceImpl getSubscription(String username, String userEmail) {
Stripe.apiKey = privateKey;
String customerId = null;
@@ -642,6 +663,25 @@ public class StripeServiceImpl implements StripeService {
} catch (StripeException e) {
throw new RuntimeException(e);
}
+ }
+
+ // 获取所有订阅
+ public List getSubscriptionIds(String username, String userEmail) {
+ Stripe.apiKey = privateKey;
+ String customerId = null;
+ try {
+ customerId = getCustomer(username, userEmail);
+ SubscriptionCollection list = Subscription.list(SubscriptionListParams.builder()
+ .setCustomer(customerId).build());
+ List data = list.getData();
+ ArrayList subscriptionIds = new ArrayList<>();
+ data.forEach(subscription -> {
+ subscriptionIds.add(subscription.getId());
+ });
+ return subscriptionIds;
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
}
@@ -747,7 +787,6 @@ public class StripeServiceImpl implements StripeService {
}
public boolean sendEmail(String subscriptionId, String type) {
-
SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO();
QueryWrapper qwSI = new QueryWrapper<>();
qwSI.eq("subscription_id", subscriptionId);
@@ -784,14 +823,7 @@ public class StripeServiceImpl implements StripeService {
emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
emailParamsDTO.setLastOrderDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodStart(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
emailParamsDTO.setEndOfPrepaidTerm(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
- emailParamsDTO.setPaymentMethod(paymentInfo.getPaymentMethod());
- emailParamsDTO.setLast4(paymentInfo.getLast4());
- emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
- emailParamsDTO.setFailMessage(orderByOrderNo.getNote());
- emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
- emailParamsDTO.setStartDate(changeTimeStampFormat(orderByOrderNo.getCreateTime()));
- emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
- emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ setSubscriptionParams(paymentInfo, subscriptionInfo, orderByOrderNo, emailParamsDTO);
SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail());
@@ -840,6 +872,79 @@ public class StripeServiceImpl implements StripeService {
return true;
}
+ public boolean sendRenewalFailEmail(String invoiceId, String subscriptionId, String orderNo){
+ // 1、确认当前订单最后一笔支付为fail
+ // 更新支付信息
+ PaymentInfo paymentInfo;
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ if (StringUtil.isNullOrEmpty(invoiceId)){
+ queryWrapper.eq("order_no", orderNo).orderByDesc("id");
+ List paymentInfos = paymentInfoService.getBaseMapper().selectList(queryWrapper);
+ if (paymentInfos.isEmpty() || !paymentInfos.get(0).getTradeState().equals("failed")){
+ return false;
+ }else {
+ paymentInfo = paymentInfos.get(0);
+ }
+ }else {
+ queryWrapper.eq("transaction_id", invoiceId);
+ paymentInfo = paymentInfoService.getBaseMapper().selectOne(queryWrapper);
+ if (Objects.isNull(paymentInfo)
+ || !paymentInfo.getTradeState().equals("failed")
+ || paymentInfo.getNotified().equals(1)){
+ return false;
+ }
+ }
+
+ // 2、确认当前订阅的状态为past_due
+ SubscriptionInfo subscriptionInfo;
+ QueryWrapper qwSI = new QueryWrapper<>();
+ if (StringUtil.isNullOrEmpty(subscriptionId)){
+ qwSI.eq("order_no", orderNo);
+ }else {
+ qwSI.eq("subscription_id", subscriptionId);
+ }
+ subscriptionInfo = subscriptionInfoMapper.selectOne(qwSI);
+ if (Objects.isNull(subscriptionInfo) || !subscriptionInfo.getStatus().equals("past_due")){
+ return false;
+ }
+
+ // 3、组参数
+ com.ai.da.mapper.primary.entity.Account account = accountMapper.selectById(subscriptionInfo.getAccountId());
+ String userName = account.getUserName();
+ String language = account.getLanguage();
+ OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(subscriptionInfo.getOrderNo());
+ SubscriptionEmailParamsDTO emailParamsDTO = new SubscriptionEmailParamsDTO();
+ emailParamsDTO.setUsername(userName);
+ emailParamsDTO.setOrderId(paymentInfo.getId().toString());
+ emailParamsDTO.setCreateDate(String.valueOf(paymentInfo.getCreateTime()).replace("T", " "));
+ emailParamsDTO.setQuantity(String.valueOf(1));
+ emailParamsDTO.setTotalFee(paymentInfo.getPayerTotal().toString());
+ setSubscriptionParams(paymentInfo, subscriptionInfo, orderByOrderNo, emailParamsDTO);
+ // todo
+ emailParamsDTO.setAccountPageRef("\"https://www.aida.com.hk/home/homePage\"");
+
+ // 4、发邮件
+ SendEmailUtil.subscriptionEmailReminder("fail_renewal", emailParamsDTO, language, account.getUserEmail());
+
+ PaymentInfo payment = new PaymentInfo();
+ payment.setId(paymentInfo.getId());
+ payment.setNotified(1);
+ payment.setUpdateTime(LocalDateTime.now());
+ paymentInfoMapper.updateById(payment);
+ return true;
+ }
+
+ private void setSubscriptionParams(PaymentInfo paymentInfo, SubscriptionInfo subscriptionInfo, OrderInfo orderByOrderNo, SubscriptionEmailParamsDTO emailParamsDTO) {
+ emailParamsDTO.setPaymentMethod(paymentInfo.getPaymentMethod());
+ emailParamsDTO.setLast4(paymentInfo.getLast4());
+ emailParamsDTO.setSubscriptionId(subscriptionInfo.getId().toString());
+ emailParamsDTO.setFailMessage(orderByOrderNo.getNote());
+ emailParamsDTO.setSubscriptionType(subscriptionInfo.getType());
+ emailParamsDTO.setStartDate(changeTimeStampFormat(orderByOrderNo.getCreateTime()));
+ emailParamsDTO.setNextPayDate(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ emailParamsDTO.setRenewalTime(changeTimeStampFormat(subscriptionInfo.getCurrentPeriodEnd(), "seconds", CommonConstant.TIME_FORMAT_MMM_dd_yyyy));
+ }
+
public void subscriptionReminder(){
// 提前7天的 00:00:00 和 23:59:59
LocalDateTime startOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atStartOfDay();
@@ -876,4 +981,87 @@ public class StripeServiceImpl implements StripeService {
}
}
+ // todo 新建一个订阅 使用不会成功的付款方式
+
+ public String createSubscriptionTemp(String name, String email){
+ Stripe.apiKey = privateKey;
+ try {
+ OrderInfo orderInfo = orderInfoService.createOrderByProductId(1, PayTypeEnum.STRIPE.getType(), ProductEnum.DailySubscription);
+
+ String paymentMethodCode = "pm_card_mastercard";
+ PaymentMethod paymentMethod = PaymentMethod.retrieve(paymentMethodCode);
+
+ String customerId = getCustomer(name, email);
+ log.info("customerId: {}", customerId);
+
+ PaymentMethodAttachParams attachParams = PaymentMethodAttachParams.builder()
+ .setCustomer(customerId)
+ .build();
+ paymentMethod.attach(attachParams);
+
+ // 设置默认付款方式
+ Customer updatedCustomer = Customer.retrieve(customerId);
+ CustomerUpdateParams params = CustomerUpdateParams.builder()
+ .setInvoiceSettings(
+ CustomerUpdateParams.InvoiceSettings.builder()
+ .setDefaultPaymentMethod(paymentMethod.getId())
+ .build()
+ )
+ .build();
+ updatedCustomer.update(params);
+
+ // 3. 创建订阅
+ SubscriptionCreateParams subscriptionParams = SubscriptionCreateParams.builder()
+ .setCustomer(customerId)
+ .addItem(
+ SubscriptionCreateParams.Item.builder()
+ .setPrice("price_1QFXkf02n1TEydyNtA4TQ3Yz") // 替换为实际的价格 ID
+ .build()
+ )
+ .setDescription("AiDA - " + orderInfo.getOrderNo())
+ .build();
+ Subscription subscription = Subscription.create(subscriptionParams);
+
+ // 打印订阅 ID
+ System.out.println("Subscription created: " + subscription.getId());
+ return subscription.getId();
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String changeCustomerPayment(String name, String email){
+ Stripe.apiKey = privateKey;
+ String paymentMethodCode = "pm_card_chargeCustomerFail";
+ try {
+ PaymentMethod paymentMethod = PaymentMethod.retrieve(paymentMethodCode);
+
+ String customerId = getCustomer(name, email);
+ log.info("customerId: {}", customerId);
+
+ // 附加支付方式到客户
+ PaymentMethodAttachParams attachParams = PaymentMethodAttachParams.builder()
+ .setCustomer(customerId)
+ .build();
+ paymentMethod.attach(attachParams);
+ // 更新客户的默认支付方式
+ CustomerUpdateParams params = CustomerUpdateParams.builder()
+ .setInvoiceSettings(
+ CustomerUpdateParams.InvoiceSettings.builder()
+ .setDefaultPaymentMethod(paymentMethod.getId())
+ .build()
+ )
+ .build();
+
+ // 更新客户信息
+ Customer customer = Customer.retrieve(customerId);
+ customer.update(params);
+
+ return paymentMethod.getId();
+
+ } catch (StripeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
}
From 1c965224474f95e8a7d3efabd7f5a1f666c79a10 Mon Sep 17 00:00:00 2001
From: shahaibo <1023316923@qq.com>
Date: Mon, 9 Dec 2024 13:31:30 +0800
Subject: [PATCH 08/91] TASK:AiDA
---
.../security/filter/AuthenticationFilter.java | 2 +-
.../ai/da/controller/AccountController.java | 6 +
.../ai/da/controller/DesignController.java | 16 +-
.../controller/SavedCollectionController.java | 6 +
.../da/controller/ThirdPartyController.java | 19 +-
.../{entity => }/AccountExtendMapper.java | 3 +-
.../primary/ProductImageAttributeMapper.java | 7 +
.../ai/da/mapper/primary/entity/Account.java | 2 +
.../primary/entity/ProductImageAttribute.java | 35 +++
.../secondary/entity/AttributeRetrieval.java | 2 -
.../ai/da/model/dto/DesignCollectionDTO.java | 4 +
.../model/dto/ProductImageInitializeDTO.java | 10 +
.../da/model/dto/ReDesignCollectionDTO.java | 2 +-
.../da/model/vo/DesignCollectionItemVO.java | 2 +
.../ai/da/model/vo/DesignCollectionVO.java | 2 +
.../com/ai/da/model/vo/ValidateElementVO.java | 4 +
.../java/com/ai/da/python/PythonService.java | 133 +++++++++-
.../ai/da/python/vo/DesignPythonObject.java | 2 +
.../ai/da/python/vo/DesignPythonObjects.java | 2 +
.../com/ai/da/service/AccountService.java | 6 +-
.../java/com/ai/da/service/DesignService.java | 11 +-
.../ai/da/service/UserLikeGroupService.java | 3 +
.../da/service/impl/AccountServiceImpl.java | 234 ++++++++++++++++-
.../impl/CollectionElementServiceImpl.java | 6 +
.../ai/da/service/impl/DesignServiceImpl.java | 246 +++++++++++++++++-
.../impl/UserLikeGroupServiceImpl.java | 53 ++++
26 files changed, 778 insertions(+), 40 deletions(-)
rename src/main/java/com/ai/da/mapper/primary/{entity => }/AccountExtendMapper.java (74%)
create mode 100644 src/main/java/com/ai/da/mapper/primary/ProductImageAttributeMapper.java
create mode 100644 src/main/java/com/ai/da/mapper/primary/entity/ProductImageAttribute.java
create mode 100644 src/main/java/com/ai/da/model/dto/ProductImageInitializeDTO.java
diff --git a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
index 05117ec4..54dbf3a6 100644
--- a/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
+++ b/src/main/java/com/ai/da/common/security/filter/AuthenticationFilter.java
@@ -51,7 +51,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
- "/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential"
+ "/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode"
);
@Override
diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java
index 0332154a..eaecde79 100644
--- a/src/main/java/com/ai/da/controller/AccountController.java
+++ b/src/main/java/com/ai/da/controller/AccountController.java
@@ -297,4 +297,10 @@ public class AccountController {
public Response accountDetail(@RequestParam("id") Long id) {
return Response.success(accountService.accountDetail(id));
}
+
+ @PostMapping("getAccountDetail")
+ @ApiOperation(value = "获取账户信息")
+ public Response getAccountDetail() {
+ return Response.success(accountService.getAccountDetail());
+ }
}
diff --git a/src/main/java/com/ai/da/controller/DesignController.java b/src/main/java/com/ai/da/controller/DesignController.java
index 2ea463c1..934bcecb 100644
--- a/src/main/java/com/ai/da/controller/DesignController.java
+++ b/src/main/java/com/ai/da/controller/DesignController.java
@@ -31,7 +31,7 @@ public class DesignController {
@ApiOperation(value = "设计 Conllection")
@PostMapping("/designCollection")
@CrossOrigin
- public Response designCollection(@Valid @RequestBody DesignCollectionDTO designDTO) {
+ public Response designCollection(@Valid @RequestBody DesignCollectionDTO designDTO) {
return Response.success(designService.designCollection(designDTO));
}
@@ -43,7 +43,7 @@ public class DesignController {
@ApiOperation(value = "重新设计 Collection")
@PostMapping("/reDesignCollection")
- public Response reDesignCollection(@Valid @RequestBody ReDesignCollectionDTO reDesignDTO) {
+ public Response reDesignCollection(@Valid @RequestBody ReDesignCollectionDTO reDesignDTO) {
return Response.success(designService.reDesignCollection(reDesignDTO));
}
@@ -83,4 +83,16 @@ public class DesignController {
return Response.success(designService.getModel(designItemIdList));
}
+ @ApiOperation(value = "获取design结果")
+ @GetMapping("/getDesignResult")
+ public Response getDesignResult(@RequestParam("requestId") String requestId, @RequestParam("objectSignList") List objectSignList){
+ return Response.success(designService.getDesignResult(requestId, objectSignList));
+ }
+
+ @ApiOperation(value = "云生成")
+ @PostMapping("/designCloud")
+ @CrossOrigin
+ public Response designCloud(@Valid @RequestBody DesignCollectionDTO designDTO) {
+ return Response.success(designService.designCloud(designDTO));
+ }
}
diff --git a/src/main/java/com/ai/da/controller/SavedCollectionController.java b/src/main/java/com/ai/da/controller/SavedCollectionController.java
index 29cbabac..1204b2c5 100644
--- a/src/main/java/com/ai/da/controller/SavedCollectionController.java
+++ b/src/main/java/com/ai/da/controller/SavedCollectionController.java
@@ -245,4 +245,10 @@ public class SavedCollectionController {
public Response likeHistoryRelSketch() {
return Response.success(userLikeGroupService.likeHistoryRelSketch());
}
+
+ @ApiOperation(value = "productImageInitialize")
+ @PostMapping("/productImageInitialize")
+ public Response productImageUpload(@Valid @RequestBody ProductImageInitializeDTO productImageInitializeDTO) {
+ return Response.success(userLikeGroupService.productImageInitialize(productImageInitializeDTO));
+ }
}
diff --git a/src/main/java/com/ai/da/controller/ThirdPartyController.java b/src/main/java/com/ai/da/controller/ThirdPartyController.java
index e0047d62..c0ff893f 100644
--- a/src/main/java/com/ai/da/controller/ThirdPartyController.java
+++ b/src/main/java/com/ai/da/controller/ThirdPartyController.java
@@ -4,7 +4,10 @@ import com.ai.da.common.response.Response;
import com.ai.da.mapper.primary.entity.GoogleUser;
import com.ai.da.model.dto.*;
import com.ai.da.model.vo.AccountLoginVO;
+import com.ai.da.model.vo.DesignCollectionVO;
import com.ai.da.service.AccountService;
+import com.ai.da.service.DesignService;
+import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -29,6 +32,9 @@ public class ThirdPartyController {
@Resource
private AccountService accountService;
+ @Resource
+ private DesignService designService;
+
/*@ApiOperation(value = "Add user information")
@PostMapping("/addUser")
public Response addUser(@Valid @RequestBody AccountAddDTO accountAddDTO) {
@@ -124,9 +130,20 @@ public class ThirdPartyController {
}
@CrossOrigin
@GetMapping("/parseGoogleCredential")
- public Response parseGoogleCredential(@RequestParam("credential") String credential) {
+ public Response parseGoogleCredential(@RequestParam("credential") String credential) {
return Response.success(accountService.parseGoogleCredential(credential));
}
+ @CrossOrigin
+ @GetMapping("/parseWeChatCode")
+ public Response parseWeChatCode(@RequestParam("code") String code) {
+ return Response.success(accountService.parseWeChatCode(code));
+ }
+ @ApiOperation(value = "接收Design结果")
+ @PostMapping("/receiveDesignResults")
+ @CrossOrigin
+ public Response receiveDesignResults(@Valid @RequestBody JSONObject responseObject) {
+ return Response.success(designService.receiveDesignResults(responseObject));
+ }
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/AccountExtendMapper.java b/src/main/java/com/ai/da/mapper/primary/AccountExtendMapper.java
similarity index 74%
rename from src/main/java/com/ai/da/mapper/primary/entity/AccountExtendMapper.java
rename to src/main/java/com/ai/da/mapper/primary/AccountExtendMapper.java
index 312fa810..687790e3 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/AccountExtendMapper.java
+++ b/src/main/java/com/ai/da/mapper/primary/AccountExtendMapper.java
@@ -1,6 +1,7 @@
-package com.ai.da.mapper.primary.entity;
+package com.ai.da.mapper.primary;
import com.ai.da.common.config.mybatis.plus.CommonMapper;
+import com.ai.da.mapper.primary.entity.AccountExtend;
import java.util.Date;
import java.util.List;
diff --git a/src/main/java/com/ai/da/mapper/primary/ProductImageAttributeMapper.java b/src/main/java/com/ai/da/mapper/primary/ProductImageAttributeMapper.java
new file mode 100644
index 00000000..65d0551a
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/ProductImageAttributeMapper.java
@@ -0,0 +1,7 @@
+package com.ai.da.mapper.primary;
+
+import com.ai.da.common.config.mybatis.plus.CommonMapper;
+import com.ai.da.mapper.primary.entity.ProductImageAttribute;
+
+public interface ProductImageAttributeMapper extends CommonMapper {
+}
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 3629eedf..dc4a93ae 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
@@ -118,4 +118,6 @@ public class Account implements Serializable {
private BigDecimal shareCredits;
private Integer subAccountNum;
+
+ private String invitationCode;
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/ProductImageAttribute.java b/src/main/java/com/ai/da/mapper/primary/entity/ProductImageAttribute.java
new file mode 100644
index 00000000..129eb32b
--- /dev/null
+++ b/src/main/java/com/ai/da/mapper/primary/entity/ProductImageAttribute.java
@@ -0,0 +1,35 @@
+package com.ai.da.mapper.primary.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("product_image_attribute")
+public class ProductImageAttribute implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+ private String imgName;
+ private String length;
+ private String sleeveLength;
+ private String sleeveShape;
+ private String sleeveShoulder;
+ private String neckline;
+ private String collar;
+ private String design;
+ private String silhouette;
+ private String type;
+ private String openingType;
+ private String subtype;
+
+ private String style;
+}
diff --git a/src/main/java/com/ai/da/mapper/secondary/entity/AttributeRetrieval.java b/src/main/java/com/ai/da/mapper/secondary/entity/AttributeRetrieval.java
index 84cc1b91..12ef2329 100644
--- a/src/main/java/com/ai/da/mapper/secondary/entity/AttributeRetrieval.java
+++ b/src/main/java/com/ai/da/mapper/secondary/entity/AttributeRetrieval.java
@@ -19,6 +19,4 @@ public class AttributeRetrieval {
private String subtype;
private String style;
-
- private Integer deprecated;
}
diff --git a/src/main/java/com/ai/da/model/dto/DesignCollectionDTO.java b/src/main/java/com/ai/da/model/dto/DesignCollectionDTO.java
index 478483c4..91120a0b 100644
--- a/src/main/java/com/ai/da/model/dto/DesignCollectionDTO.java
+++ b/src/main/java/com/ai/da/model/dto/DesignCollectionDTO.java
@@ -63,4 +63,8 @@ public class DesignCollectionDTO {
private String moodboardPostion;
+ private List requestIdList;
+
+ private Integer designNum;
+
}
diff --git a/src/main/java/com/ai/da/model/dto/ProductImageInitializeDTO.java b/src/main/java/com/ai/da/model/dto/ProductImageInitializeDTO.java
new file mode 100644
index 00000000..a3aace06
--- /dev/null
+++ b/src/main/java/com/ai/da/model/dto/ProductImageInitializeDTO.java
@@ -0,0 +1,10 @@
+package com.ai.da.model.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ProductImageInitializeDTO {
+ private List libraryIds;
+}
diff --git a/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java b/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
index b72a690a..65a125a9 100644
--- a/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
+++ b/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
@@ -33,7 +33,7 @@ public class ReDesignCollectionDTO {
@ApiModelProperty("市场手稿板图片id 数组")
private List marketingSketchs;
- @NotNull(message = "colorBoards.cannot.be.empty")
+ @NotNull(message = "systemScale.cannot.be.empty")
@ApiModelProperty("系统取图比列")
private BigDecimal systemScale;
diff --git a/src/main/java/com/ai/da/model/vo/DesignCollectionItemVO.java b/src/main/java/com/ai/da/model/vo/DesignCollectionItemVO.java
index dc15cd14..1c98e059 100644
--- a/src/main/java/com/ai/da/model/vo/DesignCollectionItemVO.java
+++ b/src/main/java/com/ai/da/model/vo/DesignCollectionItemVO.java
@@ -22,6 +22,8 @@ public class DesignCollectionItemVO {
@ApiModelProperty("t_design_python_outfit id")
private String designOutfitUrl;
+ private String objectSign;
+
public DesignCollectionItemVO() {
}
diff --git a/src/main/java/com/ai/da/model/vo/DesignCollectionVO.java b/src/main/java/com/ai/da/model/vo/DesignCollectionVO.java
index 8ad6e7a4..807f364c 100644
--- a/src/main/java/com/ai/da/model/vo/DesignCollectionVO.java
+++ b/src/main/java/com/ai/da/model/vo/DesignCollectionVO.java
@@ -23,6 +23,8 @@ public class DesignCollectionVO {
private String processId;
+ private List UnfinishedList;
+
public DesignCollectionVO() {
}
}
diff --git a/src/main/java/com/ai/da/model/vo/ValidateElementVO.java b/src/main/java/com/ai/da/model/vo/ValidateElementVO.java
index f2cb87a1..73912616 100644
--- a/src/main/java/com/ai/da/model/vo/ValidateElementVO.java
+++ b/src/main/java/com/ai/da/model/vo/ValidateElementVO.java
@@ -47,4 +47,8 @@ public class ValidateElementVO {
private String modelSex;
private String style;
+
+ private List requestIdList;
+
+ private Integer designNum;
}
diff --git a/src/main/java/com/ai/da/python/PythonService.java b/src/main/java/com/ai/da/python/PythonService.java
index 08113d91..3032a498 100644
--- a/src/main/java/com/ai/da/python/PythonService.java
+++ b/src/main/java/com/ai/da/python/PythonService.java
@@ -230,13 +230,14 @@ public class PythonService {
designPythonObjects.setProcess_id(processId);
long pinPrintNum = calculateDesignPinPrintNum(elementVO.getPrintBoardElements());
- long noPinPrintNum = calculateDesignNoPinPrintNum(elementVO.getPrintBoardElements());
- long noPrintNum = 8 - pinPrintNum - noPinPrintNum;
+ long noPinPrintNum = calculateDesignNoPinPrintNum(elementVO.getPrintBoardElements(), elementVO.getDesignNum());
+ long noPrintNum = elementVO.getDesignNum() - pinPrintNum - noPinPrintNum;
elementVO.setNoPinPrintNum(noPinPrintNum);
int[] sketchNumbers = new int[3];
- for (int i = 0; i < 8; i++) {
+ int designNum = elementVO.getDesignNum();
+ for (int i = 0; i < designNum; i++) {
CurrentDesignPictureTypeEnum designPictureType = calculateCurrentDesignPictureTypeNew(elementVO, sketchNumbers, systemScale);
if (designPictureType == null) break;
@@ -261,7 +262,7 @@ public class PythonService {
elementVO.setDesignPythonItemPrint(designPythonItemPrint);
elementVO.setDesignPrintPictureTypeLayoutList(calculateCurrentDesignPintPictureTypeLayout(elementVO.getModelSex()));
- DesignPythonObject pythonObject = createDesignPythonObject(elementVO, designPictureType, systemScale, singleOverall, switchCategory);
+ DesignPythonObject pythonObject = createDesignPythonObject(elementVO, designPictureType, systemScale, singleOverall, switchCategory, i);
objects.add(pythonObject);
redisUtil.addProcessId(processId, i + 1);
}
@@ -296,22 +297,24 @@ public class PythonService {
}
}
- private DesignPythonObject createDesignPythonObject(ValidateElementVO elementVO, CurrentDesignPictureTypeEnum designPictureType, BigDecimal systemScale, String singleOverall, String switchCategory) {
+ private DesignPythonObject createDesignPythonObject(ValidateElementVO elementVO, CurrentDesignPictureTypeEnum designPictureType, BigDecimal systemScale, String singleOverall, String switchCategory, int i) {
DesignPythonObject pythonObject = new DesignPythonObject();
pythonObject.setItems(coverToDesignPythonItemNew(elementVO, designPictureType, systemScale));
pythonObject.setBasic(coverToBasic(pythonObject.getItems().get(0), singleOverall, switchCategory, elementVO.getDesignLibraryModelPoint()));
+ pythonObject.setObjectSign(elementVO.getRequestIdList().get(i));
return pythonObject;
}
private CurrentDesignPictureTypeEnum calculateCurrentDesignPictureTypeNew(ValidateElementVO elementVO, int[] sketchNumbers, BigDecimal systemScale) {
List pinData = getPinData(elementVO);
+ Integer designNum = elementVO.getDesignNum();
if (CollectionUtil.isNotEmpty(pinData)) {
return CurrentDesignPictureTypeEnum.PIN;
} else {
if (sketchNumbers[1] == 0 && sketchNumbers[2] == 0) {
- sketchNumbers[1] = systemScale.multiply(BigDecimal.valueOf(8 - sketchNumbers[0])).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
- sketchNumbers[2] = 8 - sketchNumbers[0] - sketchNumbers[1];
+ sketchNumbers[1] = systemScale.multiply(BigDecimal.valueOf(designNum - sketchNumbers[0])).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
+ sketchNumbers[2] = designNum - sketchNumbers[0] - sketchNumbers[1];
}
if (sketchNumbers[2] > 0 && sketchNumbers[1] > 0) {
Long l = RandomsUtil.randomSysFile(0l, 2l);
@@ -491,7 +494,7 @@ public class PythonService {
}
//计算print 非Pin图片剩余张数
- private long calculateDesignNoPinPrintNum(List printBoardElements) {
+ private long calculateDesignNoPinPrintNum(List printBoardElements, Integer designNum) {
if (CollectionUtils.isEmpty(printBoardElements)) {
return 0;
}
@@ -500,10 +503,10 @@ public class PythonService {
return 0;
} else {
long pinNum = printBoardElements.stream().filter(f -> f.getHasPin() == 1).count();
- if (8 - pinNum < 4) {
- return RandomsUtil.randomSysFile(0L, 8 - pinNum + 1);
+ if (designNum - pinNum < designNum/2) {
+ return RandomsUtil.randomSysFile(0L, designNum - pinNum + 1);
} else {
- return RandomsUtil.randomSysFile(0L, 4L + 1);
+ return RandomsUtil.randomSysFile(0L, (long) (designNum/2 + 1));
}
}
}
@@ -553,7 +556,7 @@ public class PythonService {
if (elementVO.getSingleOverall().equals(SingleOverallEnum.OVERALL.getRealName())) {
List otherSketchCategoryList = getOtherSketchCategoryList(elementVO.getModelSex(), designPythonItem);
if (!otherSketchCategoryList.isEmpty()) {
- JSONObject attributeRecognition = getAttributeRecognition(designPythonItem, designPythonItem.getType(), elementVO.getModelSex());
+ JSONObject attributeRecognition = getAttributeRecognition(designPythonItem.getPath(), designPythonItem.getType(), elementVO.getModelSex());
for (String styleCategory : otherSketchCategoryList) {
DesignPythonItem otherSketch = processAttributeRecognition(attributeRecognition, elementVO, designPictureType, styleCategory, systemScale);
itemList.add(otherSketch);
@@ -790,7 +793,7 @@ public class PythonService {
return attributeRetrieval;
}
- public JSONObject getAttributeRecognition(DesignPythonItem designPythonItem, String styleCategory, String modelSex) {
+ public JSONObject getAttributeRecognition(String sketchImgUrl, String styleCategory, String modelSex) {
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
@@ -801,7 +804,7 @@ public class PythonService {
JSONObject paramJSONObject = new JSONObject();
paramJSONObject.put("category", styleCategory);
paramJSONObject.put("colony", modelSex);
- paramJSONObject.put("sketch_img_url", designPythonItem.getPath());
+ paramJSONObject.put("sketch_img_url", sketchImgUrl);
JSONArray paramArray = new JSONArray();
paramArray.add(paramJSONObject);
String param = JSON.toJSONString(paramArray, SerializerFeature.DisableCircularReferenceDetect);
@@ -3003,6 +3006,58 @@ public class PythonService {
throw new BusinessException("design.interface.exception");
}
+ public JSONObject designStream(DesignPythonObjects designPythonObjects) {
+ // todo 限流校验
+// AccessLimitUtils.validate("design",5);
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
+ .readTimeout(60, TimeUnit.SECONDS)//读取超时(单位:秒)
+ .writeTimeout(60, TimeUnit.SECONDS)//写入超时(单位:秒)
+ .build();
+ MediaType mediaType = MediaType.parse("application/json");
+ //关闭FastJson的引用检测 防止出现$ref 现象
+ String param = JSON.toJSONString(designPythonObjects, SerializerFeature.DisableCircularReferenceDetect);
+ log.info("design请求python 参数:####{}", param);
+ RequestBody body = RequestBody.create(mediaType, param);
+ Request request = new Request.Builder()
+ .url(accessPythonIp + ":" + accessPythonPort + "/api/design_v2")
+// .url(fastApiPythonAddress + "/api/design")
+// .url(accessPythonIp + ":10200/aifda/api/v1.0/generate")
+ .method("POST", body)
+ .addHeader("Authorization", "Basic YWlkbGFiOjEyMw==")
+ .addHeader("Content-Type", "application/json")
+ .build();
+ Response response;
+ String responseBody;
+ try {
+ response = client.newCall(request).execute();
+ } catch (IOException ioException) {
+ AccessLimitUtils.validateOut("design");
+ log.error("PythonService##design异常###{}", ExceptionUtil.getThrowableList(ioException));
+ throw new BusinessException("design.interface.exception");
+ }
+ //去除限流
+// AccessLimitUtils.validateOut("design");
+ if (response.isSuccessful()) {
+ try {
+ if (Objects.nonNull(response.body())) {
+ responseBody = response.body().string();
+ JSONObject responseObject = JSON.parseObject(responseBody);
+ log.info("PythonService##responseObject###{}", responseObject);
+ return responseObject;
+ }
+ throw new BusinessException("design.interface.exception");
+ } catch (IOException | JSONException e) {
+ log.error("PythonService##design异常###{}", e.getMessage());
+ throw new BusinessException("design.interface.exception");
+ }
+ }
+ log.error("PythonService##design异常response###{}", response);
+ //生成失败
+ throw new BusinessException("design.interface.exception");
+ }
+
/**
* 暂时未用
*/
@@ -3566,4 +3621,54 @@ public class PythonService {
//生成失败
throw new BusinessException("bright.interface.exception");
}
+
+ public JSONObject attributeRecognition(List pictureUrls,List ids, List category) {
+ //限流校验
+ AccessLimitUtils.validate("attributeRecognition", 20);
+ OkHttpClient client = new OkHttpClient().newBuilder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
+ .readTimeout(300, TimeUnit.SECONDS)//读取超时(单位:秒)
+ .writeTimeout(300, TimeUnit.SECONDS)//写入超时(单位:秒)
+ .build();
+ MediaType mediaType = MediaType.parse("application/json");
+ Map> content = Maps.newHashMap();
+ //识别图片路径数组
+ content.put("upload_img_path", pictureUrls);
+ //识别图片id数组
+ content.put("upload_img_id", ids);
+ content.put("upload_img_category", category);
+ RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(content));
+ Request request = new Request.Builder()
+ .url(accessPythonIp + ":9993/api/attribute")
+ .method("POST", body)
+ .addHeader("Authorization", "Basic YWlkbGFiOjEyMw==")
+ .addHeader("Content-Type", "application/json")
+ .build();
+ Response response = null;
+ String bodyStr = null;
+ try {
+ log.info("识别python对应的属性标签值请求入参content###{}", JSON.toJSONString(content));
+ response = client.newCall(request).execute();
+ bodyStr = response.body().string();
+ } catch (IOException ioException) {
+ log.error("PythonService###attributeRecognition异常##{}", ExceptionUtil.getThrowableList(ioException));
+ }
+ log.info("识别python对应的属性标签值结果###{}",bodyStr.trim());
+ //去除限流
+ AccessLimitUtils.validateOut("attributeRecognition");
+ if (Objects.isNull(response)) {
+ log.error("PythonService##attributeRecognition异常###{}", "response or body is empty!");
+ throw new BusinessException("attribute recognition exception!");
+ }
+ JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(response));
+ Boolean result = jsonObject.getBoolean("successful");
+ if (result) {
+ JSONObject attributeJSONObject = JSON.parseObject(bodyStr.trim());
+ return attributeJSONObject;
+ }
+ log.info("识别python对应的属性标签值异常###{}", jsonObject);
+ //生成失败
+ throw new BusinessException("Atribute recognition exception!");
+ }
}
diff --git a/src/main/java/com/ai/da/python/vo/DesignPythonObject.java b/src/main/java/com/ai/da/python/vo/DesignPythonObject.java
index aa6a11e2..12946423 100644
--- a/src/main/java/com/ai/da/python/vo/DesignPythonObject.java
+++ b/src/main/java/com/ai/da/python/vo/DesignPythonObject.java
@@ -15,4 +15,6 @@ public class DesignPythonObject {
* basic 选项
*/
DesignPythonBasic basic;
+
+ private String objectSign;
}
diff --git a/src/main/java/com/ai/da/python/vo/DesignPythonObjects.java b/src/main/java/com/ai/da/python/vo/DesignPythonObjects.java
index af5fa3be..1f61ee07 100644
--- a/src/main/java/com/ai/da/python/vo/DesignPythonObjects.java
+++ b/src/main/java/com/ai/da/python/vo/DesignPythonObjects.java
@@ -16,4 +16,6 @@ public class DesignPythonObjects {
* design新增的library
*/
List addLibrary;
+
+ private String requestId;
}
diff --git a/src/main/java/com/ai/da/service/AccountService.java b/src/main/java/com/ai/da/service/AccountService.java
index 2d86fdf2..64bde1da 100644
--- a/src/main/java/com/ai/da/service/AccountService.java
+++ b/src/main/java/com/ai/da/service/AccountService.java
@@ -201,5 +201,9 @@ public interface AccountService extends IService {
Account accountDetail(Long id);
- GoogleUser parseGoogleCredential(String credential);
+ AccountLoginVO parseGoogleCredential(String credential);
+
+ AccountLoginVO parseWeChatCode(String code);
+
+ AccountLoginVO getAccountDetail();
}
diff --git a/src/main/java/com/ai/da/service/DesignService.java b/src/main/java/com/ai/da/service/DesignService.java
index b346c8bf..d362d27b 100644
--- a/src/main/java/com/ai/da/service/DesignService.java
+++ b/src/main/java/com/ai/da/service/DesignService.java
@@ -7,6 +7,7 @@ import com.ai.da.model.vo.DesignCollectionVO;
import com.ai.da.model.vo.DesignItemDetailVO;
import com.ai.da.model.vo.DesignLikeVO;
import com.ai.da.python.vo.DesignPythonObjects;
+import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
@@ -25,7 +26,7 @@ public interface DesignService extends IService {
* @param designDTO
* @return
*/
- DesignCollectionVO designCollection(DesignCollectionDTO designDTO);
+ String designCollection(DesignCollectionDTO designDTO);
/**
* redesign
@@ -33,7 +34,7 @@ public interface DesignService extends IService {
* @param reDesignDTO
* @return
*/
- DesignCollectionVO reDesignCollection(ReDesignCollectionDTO reDesignDTO);
+ String reDesignCollection(ReDesignCollectionDTO reDesignDTO);
/**
* redesign
@@ -99,4 +100,10 @@ public interface DesignService extends IService {
List getModel(List designItemIdList);
Long getCountByUserAndTime(String startTime, String endTime, List accountIds);
+
+ Boolean receiveDesignResults(JSONObject responseObject);
+
+ DesignCollectionVO getDesignResult(String requestId, List objectSignList);
+
+ String designCloud(DesignCollectionDTO designDTO);
}
diff --git a/src/main/java/com/ai/da/service/UserLikeGroupService.java b/src/main/java/com/ai/da/service/UserLikeGroupService.java
index 71edbc50..fb8feb6a 100644
--- a/src/main/java/com/ai/da/service/UserLikeGroupService.java
+++ b/src/main/java/com/ai/da/service/UserLikeGroupService.java
@@ -4,6 +4,7 @@ import com.ai.da.mapper.primary.entity.CanvasElementUpload;
import com.ai.da.mapper.primary.entity.ToProductImageResult;
import com.ai.da.mapper.primary.entity.UserLikeGroup;
import com.ai.da.model.dto.ExportSaveDTO;
+import com.ai.da.model.dto.ProductImageInitializeDTO;
import com.ai.da.model.dto.ProductImageLikeDTO;
import com.ai.da.model.dto.ToProductImageDTO;
import com.ai.da.model.vo.*;
@@ -62,4 +63,6 @@ public interface UserLikeGroupService extends IService {
List getRelightResult(List taskIdList);
String likeHistoryRelSketch();
+
+ Boolean productImageInitialize(ProductImageInitializeDTO productImageInitializeDTO);
}
diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
index 03145804..13ec162e 100644
--- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
@@ -11,6 +11,7 @@ import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.*;
+import com.ai.da.mapper.primary.AccountExtendMapper;
import com.ai.da.mapper.primary.AccountMapper;
import com.ai.da.mapper.primary.QuestionnaireMapper;
import com.ai.da.mapper.primary.TrialOrderMapper;
@@ -21,6 +22,7 @@ import com.ai.da.model.enums.Language;
import com.ai.da.model.vo.*;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -30,9 +32,6 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
-import com.google.auth.oauth2.IdToken;
-import com.google.auth.oauth2.TokenVerifier;
-import com.google.common.base.Function;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.netty.util.internal.StringUtil;
@@ -1202,9 +1201,10 @@ public class AccountServiceImpl extends ServiceImpl impl
if (StringUtils.isBlank(accountDesignWorksRegisterDTO.getLanguage())) {
account.setLanguage(Language.ENGLISH.name());
}
- account.setIsTrial(0);
+ account.setIsTrial(1);
account.setIsBeginner(1);
account.setValidStartTime(Instant.now().toEpochMilli());
+ toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli());
account.setCreateDate(new Date());
account.setCredits(BigDecimal.valueOf(0));
accountMapper.insert(account);
@@ -2099,7 +2099,7 @@ public class AccountServiceImpl extends ServiceImpl impl
}
@Override
- public GoogleUser parseGoogleCredential(String credential) {
+ public AccountLoginVO parseGoogleCredential(String credential) {
try {
// 配置 Google ID Token 验证器
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
@@ -2111,6 +2111,7 @@ public class AccountServiceImpl extends ServiceImpl impl
// 验证并解析 ID Token
GoogleIdToken idToken = verifier.verify(credential);
+
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
@@ -2122,7 +2123,54 @@ public class AccountServiceImpl extends ServiceImpl impl
log.info(email);
log.info(name);
- return new GoogleUser();
+ // 检查数据库中是否已有该用户
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.lambda().eq(Account::getUserEmail, email); // 根据邮箱查询用户
+ List accounts = accountMapper.selectList(queryWrapper);
+ Account account = new Account();
+ if (CollectionUtil.isNotEmpty(accounts)) {
+ account = accounts.get(0);
+ } else {
+ // 用户不存在,创建新用户(自动注册)
+ Account newUser = new Account();
+ newUser.setUserEmail(email);
+ newUser.setUserName(name);
+ newUser.setUserPassword("Third-000000");
+ newUser.setLanguage(Language.ENGLISH.name());
+ newUser.setValidStartTime(System.currentTimeMillis());
+ newUser.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
+ newUser.setCreateDate(new Date());
+ newUser.setIsTrial(1);
+ newUser.setIsBeginner(1);
+ newUser.setCredits(BigDecimal.valueOf(100));
+ newUser.setSystemUser(3);
+ accountMapper.insert(newUser);
+
+ account = newUser;
+ }
+
+ AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
+ response.setEmail(account.getUserEmail());
+ String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
+ if (StringUtils.isNotBlank(token)) {
+ //用户已登入
+ response.setToken(token);
+ } else {
+ response.setToken(createAccountToken(account));
+ }
+ response.setUserId(account.getId());
+ response.setSystemUser(account.getSystemUser());
+ // 设置头像
+ String avatar;
+ if (StringUtil.isNullOrEmpty(account.getAvatar())){
+ avatar = CommonConstant.DEFAULT_AVATAR;
+ }else {
+ avatar = account.getAvatar();
+ }
+ response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
+ response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
+ response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ return response;
} else {
throw new IllegalArgumentException("Invalid ID token.");
}
@@ -2132,4 +2180,178 @@ public class AccountServiceImpl extends ServiceImpl impl
throw new RuntimeException("Failed to verify ID token: " + e.getMessage());
}
}
+
+ private static final String WECHAT_ACCESS_TOKEN_URL =
+ "https://api.weixin.qq.com/sns/oauth2/access_token";
+
+ private static final String APP_ID = "wxcfb92eb28d6385f5";
+ private static final String APP_SECRET = "e5592c691756455b2d03ebfd21fc3131";
+
+ @Override
+ public AccountLoginVO parseWeChatCode(String code) {
+ // 1. 获取 access_token 和 openid
+ JSONObject accessTokenResponse = getAccessTokenFromWeChat(code);
+ String accessToken = accessTokenResponse.getString("access_token");
+ String openId = accessTokenResponse.getString("openid");
+
+ if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)) {
+ throw new RuntimeException("微信接口返回数据缺失: " + accessTokenResponse.toJSONString());
+ }
+
+ // 2. 获取用户信息
+ JSONObject userInfoResponse = getUserInfoFromWeChat(accessToken, openId);
+
+ // 提取 unionid 和 nickname
+ String unionId = userInfoResponse.getString("unionid");
+ String userName = userInfoResponse.getString("nickname");
+ if (unionId == null) {
+ throw new IllegalArgumentException("无法获取 unionid,请检查微信开发平台配置");
+ }
+
+ // 检查数据库中是否已有该unionid
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.lambda().eq(AccountExtend::getAuthType, "WeChat");
+ queryWrapper.lambda().eq(AccountExtend::getAuth, unionId);
+ List accountExtends = accountExtendMapper.selectList(queryWrapper);
+ Account account = new Account();
+ if (CollectionUtil.isEmpty(accountExtends)) {
+ AccountExtend accountExtendInsert = new AccountExtend();
+ accountExtendInsert.setAuth(unionId);
+ accountExtendInsert.setAuthType("WeChat");
+
+ // 用户不存在,创建新用户(自动注册)
+ Account newUser = new Account();
+// newUser.setUserEmail(email);
+ newUser.setUserName(userName);
+ newUser.setUserPassword("Third-000000");
+ newUser.setLanguage(Language.ENGLISH.name());
+ newUser.setValidStartTime(System.currentTimeMillis());
+ newUser.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
+ newUser.setCreateDate(new Date());
+ newUser.setIsTrial(1);
+ newUser.setIsBeginner(1);
+ newUser.setCredits(BigDecimal.valueOf(100));
+ newUser.setSystemUser(3);
+ accountMapper.insert(newUser);
+
+ accountExtendInsert.setAccountId(newUser.getId());
+ accountExtendMapper.insert(accountExtendInsert);
+
+ account = newUser;
+ }else {
+ AccountExtend accountExtend = accountExtends.get(0);
+ account = accountMapper.selectById(accountExtend.getAccountId());
+ }
+
+ AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
+ response.setEmail(account.getUserEmail());
+ String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
+ if (StringUtils.isNotBlank(token)) {
+ //用户已登入
+ response.setToken(token);
+ } else {
+ response.setToken(createAccountToken(account));
+ }
+ response.setUserId(account.getId());
+ response.setSystemUser(account.getSystemUser());
+ // 设置头像
+ String avatar;
+ if (StringUtil.isNullOrEmpty(account.getAvatar())){
+ avatar = CommonConstant.DEFAULT_AVATAR;
+ }else {
+ avatar = account.getAvatar();
+ }
+ response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
+ response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
+ response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ return response;
+
+// // 2. 根据 unionid 检查用户是否存在
+// User user = userRepository.findByUnionid(unionid);
+// if (user == null) {
+// // 用户不存在,进行注册
+// user = new User();
+// user.setUnionid(unionid);
+// user.setOpenid(weChatResponse.getString("openid"));
+// user.setNickname(weChatResponse.getString("nickname"));
+// user.setAvatar(weChatResponse.getString("headimgurl"));
+// userRepository.save(user);
+// }
+//
+// // 3. 返回 unionid
+// return unionid;
+ }
+
+ private static final String WECHAT_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";
+ private JSONObject getUserInfoFromWeChat(String accessToken, String openId) {
+ // 构造微信用户信息接口的 URL
+ String url = String.format(
+ "%s?access_token=%s&openid=%s&lang=zh_CN",
+ WECHAT_USER_INFO_URL, accessToken, openId
+ );
+
+ // 调用微信接口
+ RestTemplate restTemplate = new RestTemplate();
+ String response = restTemplate.getForObject(url, String.class);
+
+ // 转换为 JSON 对象
+ JSONObject jsonResponse = JSONObject.parseObject(response);
+ if (jsonResponse.containsKey("errcode")) {
+ throw new RuntimeException("微信用户信息接口调用失败: " + jsonResponse.getString("errmsg"));
+ }
+
+ return jsonResponse;
+ }
+
+ private JSONObject getAccessTokenFromWeChat(String code) {
+ // 构造微信接口请求 URL
+ String url = String.format(
+ "%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
+ WECHAT_ACCESS_TOKEN_URL, APP_ID, APP_SECRET, code
+ );
+
+ // 调用微信接口
+ RestTemplate restTemplate = new RestTemplate();
+ String response = restTemplate.getForObject(url, String.class);
+
+ // 转换为 JSON 对象
+ JSONObject jsonResponse = JSONObject.parseObject(response);
+ if (jsonResponse.containsKey("errcode")) {
+ throw new RuntimeException("微信接口调用失败: " + jsonResponse.getString("errmsg"));
+ }
+
+ return jsonResponse;
+ }
+
+ @Override
+ public AccountLoginVO getAccountDetail() {
+ AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
+ Long accountId = authPrincipalVo.getId();
+ Account account = accountMapper.selectById(accountId);
+
+ AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
+ response.setEmail(account.getUserEmail());
+ String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
+ if (StringUtils.isNotBlank(token)) {
+ //用户已登入
+ response.setToken(token);
+ } else {
+ response.setToken(createAccountToken(account));
+ }
+ response.setUserId(account.getId());
+ response.setSystemUser(account.getSystemUser());
+ // 设置头像
+ String avatar;
+ if (StringUtil.isNullOrEmpty(account.getAvatar())){
+ avatar = CommonConstant.DEFAULT_AVATAR;
+ }else {
+ avatar = account.getAvatar();
+ }
+ response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
+ response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
+ response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ //判断是否常用ip 不是则发邮件提示
+// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
+ return response;
+ }
}
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..ca26b261 100644
--- a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java
@@ -607,6 +607,12 @@ public class CollectionElementServiceImpl extends ServiceImpl impleme
@Resource
private RedisUtil redisUtil;
+ private final ConcurrentHashMap> designContext = new ConcurrentHashMap<>();
+
+
@Override
- public DesignCollectionVO designCollection(DesignCollectionDTO designDTO) {
+ public String designCollection(DesignCollectionDTO designDTO) {
AuthPrincipalVo userInfo = UserContext.getUserHolder();
//校验collection element
ValidateElementVO elementVO = collectionElementService.validateElement(designDTO);
@@ -283,7 +287,7 @@ public class DesignServiceImpl extends ServiceImpl impleme
// return saveDesignItemAndDetail(pythonObjects, designId, collectionId, userInfo, designDTO.getTimeZone());
// }
- private DesignCollectionVO designOrRedesignOperateNew(DesignCollectionDTO designDTO, AuthPrincipalVo userInfo,
+ private String designOrRedesignOperateNew(DesignCollectionDTO designDTO, AuthPrincipalVo userInfo,
Long collectionIdParam, ValidateElementVO elementVO) {
if (CollectionUtil.isNotEmpty(designDTO.getSketchBoards())) {
//编辑sketchBoard
@@ -336,7 +340,9 @@ public class DesignServiceImpl extends ServiceImpl impleme
log.info("增加image_id关联运行时间:" + totalTimeInSeconds + " 秒");
//design
startTime = System.currentTimeMillis();
- JSONObject responseJSONObject = pythonService.designNew(pythonObjects);
+ String requestId = UUID.randomUUID().toString();
+ pythonObjects.setRequestId(requestId);
+ JSONObject responseJSONObject = pythonService.designStream(pythonObjects);
endTime = System.currentTimeMillis();
totalTimeInSeconds = (endTime - startTime) / 1000;
log.info("design python端运行时间:" + totalTimeInSeconds + " 秒");
@@ -350,10 +356,26 @@ public class DesignServiceImpl extends ServiceImpl impleme
endTime = System.currentTimeMillis();
totalTimeInSeconds = (endTime - startTime) / 1000;
log.info("处理关联关系运行时间:" + totalTimeInSeconds + " 秒");
+
+
+ Map context = new HashMap<>();
+ context.put("pythonObjects", pythonObjects); // 转换后的 Python 请求参数
+ context.put("designId", designId); // 设计 ID
+ context.put("collectionId", collectionId); // 集合 ID
+ context.put("userInfo", userInfo); // 用户信息
+ context.put("timeZone", designDTO.getTimeZone()); // 时区
+ context.put("singleOverall", designDTO.getSingleOverall()); // 其他设计参数
+ context.put("requestIdList", elementVO.getRequestIdList());
+
+ // 将上下文存入全局设计上下文中
+ designContext.put(requestId, context);
+
//保存python返回信息;保存designItem和detail
- return savePythonDesignItemAndDetail(pythonObjects, designId, collectionId, userInfo, designDTO.getTimeZone(), responseJSONObject, designDTO.getSingleOverall());
+// return savePythonDesignItemAndDetail(pythonObjects, designId, collectionId, userInfo, designDTO.getTimeZone(), responseJSONObject, designDTO.getSingleOverall());
+ return requestId;
}
+
@Override
public void relationImageId(DesignPythonObjects pythonObjects) {
if (Objects.isNull(pythonObjects)) {
@@ -601,6 +623,152 @@ public class DesignServiceImpl extends ServiceImpl impleme
@Resource
private MinioUtil minioUtil;
+ private DesignCollectionVO savePythonDesignItemAndDetailSingle(DesignPythonObjects pythonObjects, Long designId, Long collectionId, AuthPrincipalVo userInfo, String timeZone, JSONObject outfit, String singleOverall, Map context) {
+ Object designCollectionVO = context.get("DesignCollectionVO");
+ DesignCollectionVO response;
+ List designCollectionItems = Lists.newArrayList();
+ if (null == designCollectionVO) {
+ response = new DesignCollectionVO();
+ response.setDesignId(designId);
+ response.setCollectionId(collectionId);
+ response.setProcessId(pythonObjects.getProcess_id());
+ }else {
+ response = (DesignCollectionVO) designCollectionVO;
+ designCollectionItems = response.getDesignCollectionItems();
+ }
+
+ response.setDesignCollectionItems(designCollectionItems);
+
+
+ DesignPythonObject item = new DesignPythonObject();
+ String objectSign = outfit.getString("objectSign");
+ log.info(objectSign);
+ for (DesignPythonObject object : pythonObjects.getObjects()) {
+ if (object.getObjectSign().equals(objectSign)) {
+ item = object;
+ continue;
+ }
+ }
+
+
+ DesignItem designItem = new DesignItem();
+ designItem.setAccountId(userInfo.getId());
+ designItem.setCollectionId(collectionId);
+ designItem.setDesignId(designId);
+ designItem.setCreateDate(DateUtil.getByTimeZone(timeZone));
+ //生成的八张图片
+ designItem.setDesignUrl(item.getBasic().getSave_name());
+ designItem.setHasLike((byte) 0);
+ //生成designItem
+ Long designItemId = designItemService.saveOne(designItem);
+ // python design返回入库及封装
+// JSONObject outfit = data.getJSONObject(i + "");
+// if (null == outfit) {
+// continue;
+// }
+ TDesignPythonOutfit designPythonOutfit = new TDesignPythonOutfit();
+ designPythonOutfit.setDesignItemId(designItemId);
+ designPythonOutfit.setUserId(userInfo.getId());
+ designPythonOutfit.setDesignId(designId);
+ designPythonOutfit.setCollectionId(collectionId);
+ String synthesisUrl = outfit.getString("synthesis_url");
+ if (!StringUtils.isEmpty(synthesisUrl)) {
+ designPythonOutfit.setDesignUrl(synthesisUrl);
+ } else {
+ throw new BusinessException("design.interface.exception");
+ }
+ designPythonOutfitService.save(designPythonOutfit);
+
+ JSONArray layers = outfit.getJSONArray("layers");
+ List list = new ArrayList<>();
+ DesignCollectionItemVO designCollectionItemVO = new DesignCollectionItemVO();
+ designCollectionItemVO.setObjectSign(objectSign);
+ for (int i1 = 0; i1 < layers.size(); i1++) {
+ JSONObject jsonObject = layers.getJSONObject(i1);
+ TDesignPythonOutfitDetail designPythonOutfitDetail = new TDesignPythonOutfitDetail();
+ designPythonOutfitDetail.setDesignId(designId);
+ designPythonOutfitDetail.setDesignPythonOutfitId(designPythonOutfit.getId());
+ designPythonOutfitDetail.setPosition(jsonObject.getString("position"));
+ designPythonOutfitDetail.setImageSize(jsonObject.getString("image_size"));
+ designPythonOutfitDetail.setImageUrl(jsonObject.getString("image_url"));
+ if (singleOverall.equals(SingleOverallEnum.SINGLE.getRealName())) {
+ designCollectionItemVO.setDesignItemUrl(designItem.getDesignUrl());
+ }
+ designPythonOutfitDetail.setImageCategory(jsonObject.getString("image_category"));
+ designPythonOutfitDetail.setMaskUrl(jsonObject.getString("mask_url"));
+ designPythonOutfitDetail.setUserId(userInfo.getId());
+ designPythonOutfitDetail.setPriority(Integer.parseInt(jsonObject.getString("priority")));
+ designPythonOutfitDetail.setCreateDate(LocalDateTime.now());
+ list.add(designPythonOutfitDetail);
+ }
+ designPythonOutfitDetailService.saveBatch(list);
+ designCollectionItemVO.setDesignItemId(designItemId);
+ designCollectionItemVO.setDesignItemUrl(designItem.getDesignUrl());
+ designCollectionItemVO.setDesignOutfitId(designPythonOutfit.getId());
+ String designUrl = designPythonOutfit.getDesignUrl();
+ if (!StringUtils.isEmpty(designUrl) && designUrl.contains("/")) {
+ int firstIndex = designUrl.indexOf("/");
+ designCollectionItemVO.setDesignOutfitUrl(minioUtil.getPreSignedUrl(designUrl.substring(0, firstIndex) + "/" + designUrl.substring(firstIndex + 1), 24 * 60));
+ }
+ //response
+ designCollectionItems.add(designCollectionItemVO);
+
+ List designItemDetails = Lists.newArrayList();
+ Map typePriority = list.stream().collect(Collectors.toMap(d -> d.getImageCategory().split("_")[0],
+ d -> Math.abs(d.getPriority()),
+ (existing, replacement) -> replacement));
+ Map typeAndUndividedLayer = designItemService.setTypeAndUndividedLayer(layers);
+ for (DesignPythonItem detail : item.getItems()) {
+ if (null == detail) {
+ continue;
+ }
+ DesignItemDetail designItemDetail = CopyUtil.copyObject(detail, DesignItemDetail.class);
+ designItemDetail.setAccountId(userInfo.getId());
+ designItemDetail.setDesignId(designId);
+ designItemDetail.setDesignItemId(designItemId);
+ designItemDetail.setCollectionElementId(detail.getElementId());
+ designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
+ designItemDetail.setUndividedLayer(typeAndUndividedLayer.get(designItemDetail.getType().toLowerCase()));
+ if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
+ designItemDetail.setPath(detail.getBody_path());
+ //BODY不关联businessId
+ designItemDetail.setBusinessId(0L);
+ }
+ designItemDetail.setIconPath(detail.getIcon());
+ designItemDetail.setPriority(typePriority.get(detail.getType().toLowerCase()));
+ if (!detail.getType().equals("Body")){
+ DesignPythonItemPrint printObject = detail.getPrint().getOverall();
+// designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
+ designItemDetail.setPrintPath(CollectionUtils.isEmpty(printObject.getPrint_path_list()) ? "" : printObject.getPrint_path_list().get(0));
+ }
+ designItemDetailService.save(designItemDetail);
+ if (!SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType()) && !StringUtil.isNullOrEmpty(designItemDetail.getPrintPath())) {
+ DesignItemDetailPrint print = new DesignItemDetailPrint();
+ print.setDesignItemDetailId(designItemDetail.getId());
+ print.setPrintType("print");
+ print.setPath(designItemDetail.getPrintPath());
+ print.setSingleOrOverall("overall");
+ print.setPosition("[0.0,0.0]");
+// print.setScale(1d);
+ // todo mark 将print默认scale置为0.3
+ print.setScale(0.3d);
+ print.setAngle(0.0);
+ print.setPriority(1);
+ QueryWrapper getPrintboardLevel2TypeQw = new QueryWrapper<>();
+ getPrintboardLevel2TypeQw.lambda().eq(CollectionElement::getUrl, print.getPath());
+ getPrintboardLevel2TypeQw.lambda().orderByDesc(CollectionElement::getCreateDate);
+ getPrintboardLevel2TypeQw.last("limit 1");
+ CollectionElement one = collectionElementService.getOne(getPrintboardLevel2TypeQw);
+ print.setLevel2Type(one.getLevel2Type());
+ print.setCreateDate(LocalDateTime.now());
+ designItemDetailPrintService.save(print);
+ }
+ }
+ synchronized (context) {
+ context.put("DesignCollectionVO", response);
+ }
+ return response;
+ }
private DesignCollectionVO savePythonDesignItemAndDetail(DesignPythonObjects pythonObjects
, Long designId, Long collectionId, AuthPrincipalVo userInfo, String timeZone, JSONObject responseJSONObject, String singleOverall) {
DesignCollectionVO response = new DesignCollectionVO();
@@ -612,6 +780,7 @@ public class DesignServiceImpl extends ServiceImpl impleme
if (data == null) {
throw new BusinessException("design.interface.exception");
}
+
for (int i = 0; i < pythonObjects.getObjects().size(); i++) {
DesignPythonObject item = pythonObjects.getObjects().get(i);
DesignItem designItem = new DesignItem();
@@ -641,6 +810,7 @@ public class DesignServiceImpl extends ServiceImpl impleme
throw new BusinessException("design.interface.exception");
}
designPythonOutfitService.save(designPythonOutfit);
+
JSONArray layers = outfit.getJSONArray("layers");
List list = new ArrayList<>();
DesignCollectionItemVO designCollectionItemVO = new DesignCollectionItemVO();
@@ -750,7 +920,7 @@ public class DesignServiceImpl extends ServiceImpl impleme
//生成designItem
Long designItemId = designItemService.saveOne(designItem);
//response
- designCollectionItems.add(new DesignCollectionItemVO(designItemId, designItem.getDesignUrl(), null, null));
+ designCollectionItems.add(new DesignCollectionItemVO(designItemId, designItem.getDesignUrl(), null, null, null));
List designItemDetails = Lists.newArrayList();
item.getItems().forEach(detail -> {
@@ -797,7 +967,7 @@ public class DesignServiceImpl extends ServiceImpl impleme
}
@Override
- public DesignCollectionVO reDesignCollection(ReDesignCollectionDTO reDesignDTO) {
+ public String reDesignCollection(ReDesignCollectionDTO reDesignDTO) {
//校验collection
Collection collection = collectionService.getById(reDesignDTO.getCollectionId());
if (Objects.isNull(collection)) {
@@ -849,15 +1019,15 @@ public class DesignServiceImpl extends ServiceImpl impleme
}
List designItems = designItemService.getByDesignId(designId);
if (CollectionUtils.isEmpty(designItems)) {
- return new DesignCollectionVO(designId, design.getCollectionId(), null, null);
+ return new DesignCollectionVO(designId, design.getCollectionId(), null, null, null);
}
- return new DesignCollectionVO(designId, design.getCollectionId(), coverDesignItemToVO(designItems), null);
+ return new DesignCollectionVO(designId, design.getCollectionId(), coverDesignItemToVO(designItems), null, null);
}
private List coverDesignItemToVO(List designItems) {
List response = Lists.newArrayList();
designItems.forEach(designItem -> {
- response.add(new DesignCollectionItemVO(designItem.getId(), designItem.getDesignUrl(), null, null));
+ response.add(new DesignCollectionItemVO(designItem.getId(), designItem.getDesignUrl(), null, null, null));
});
return response;
}
@@ -1389,4 +1559,62 @@ public class DesignServiceImpl extends ServiceImpl impleme
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean receiveDesignResults(JSONObject responseJSONObject) {
+ String requestId = "UUID.randomUUID().toString()";
+// String requestId = responseJSONObject.getString("requestId");
+ Map context;
+ synchronized (designContext) {
+ log.info(designContext.toString());
+ context = designContext.get(requestId);
+ if (context == null) {
+ log.error("上下文数据缺失,无法完成操作");
+ return false;
+ }
+
+ DesignPythonObjects pythonObjects = (DesignPythonObjects) context.get("pythonObjects");
+ Long designId = (Long) context.get("designId");
+ Long collectionId = (Long) context.get("collectionId");
+ AuthPrincipalVo userInfo = (AuthPrincipalVo) context.get("userInfo");
+ String timeZone = (String) context.get("timeZone");
+ String singleOverall = (String) context.get("singleOverall");
+
+ DesignCollectionVO designCollectionVO = savePythonDesignItemAndDetailSingle(pythonObjects, designId, collectionId, userInfo, timeZone, responseJSONObject, singleOverall, context);
+
+ log.info(designContext.toString());
+ designContext.put(requestId, context);
+ }
+
+ return Boolean.TRUE;
+ }
+
+ @Override
+ public DesignCollectionVO getDesignResult(String requestId, List objectSignList) {
+// Map stringObjectMap = designContext.get("UUID.randomUUID().toString()");
+ Map stringObjectMap = designContext.get(requestId);
+ log.info(stringObjectMap.toString());
+ DesignCollectionVO result = (DesignCollectionVO) stringObjectMap.get("DesignCollectionVO");
+ if (Objects.isNull(result)) {
+ DesignCollectionVO noneResult = new DesignCollectionVO();
+ noneResult.setUnfinishedList(objectSignList);
+ return noneResult;
+ }
+ for (DesignCollectionItemVO designCollectionItem : result.getDesignCollectionItems()) {
+ String objectSign = designCollectionItem.getObjectSign();
+ objectSignList.remove(objectSign);
+ }
+ result.setUnfinishedList(objectSignList);
+ return result;
+ }
+
+ @Override
+ public String designCloud(DesignCollectionDTO designDTO) {
+ AuthPrincipalVo userInfo = UserContext.getUserHolder();
+ //校验collection element
+ ValidateElementVO elementVO = collectionElementService.validateElement(designDTO);
+ //design
+ return designOrRedesignOperateNew(designDTO, userInfo, null, elementVO);
+ }
+
}
diff --git a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java
index 2c51cfa6..8f3e6dbf 100644
--- a/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/UserLikeGroupServiceImpl.java
@@ -9,7 +9,9 @@ import com.ai.da.common.utils.*;
import com.ai.da.mapper.primary.*;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.mapper.secondary.AttributeRetrievalMapper;
+import com.ai.da.mapper.secondary.entity.AttributeRecognitionJSON;
import com.ai.da.model.dto.PortfolioDTO;
+import com.ai.da.model.dto.ProductImageInitializeDTO;
import com.ai.da.model.dto.ProductImageLikeDTO;
import com.ai.da.model.dto.ToProductImageDTO;
import com.ai.da.model.vo.*;
@@ -79,6 +81,8 @@ public class UserLikeGroupServiceImpl extends ServiceImpl
Date: Mon, 9 Dec 2024 16:53:29 +0800
Subject: [PATCH 09/91] =?UTF-8?q?Affiliate-=E6=96=B0=E5=A2=9E=E3=80=81?=
=?UTF-8?q?=E6=9F=A5=E8=AF=A2=E3=80=81=E4=BD=A3=E9=87=91=E8=AE=A1=E7=AE=97?=
=?UTF-8?q?=E7=AD=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ai/da/common/constant/CommonConstant.java | 2 +
.../com/ai/da/common/utils/RedisUtil.java | 14 ++
.../com/ai/da/common/utils/SendEmailUtil.java | 120 ++++++++---
.../ai/da/controller/AffiliateController.java | 63 ++++++
.../ai/da/controller/ElementController.java | 2 +
.../ai/da/controller/StripeController.java | 6 +
.../ai/da/mapper/primary/AffiliateMapper.java | 7 +
.../ai/da/mapper/primary/entity/Account.java | 3 +
.../da/mapper/primary/entity/Affiliate.java | 28 +++
.../da/mapper/primary/entity/OrderInfo.java | 5 +
.../da/model/dto/AffiliateEmailParamsDTO.java | 31 +++
.../ai/da/model/dto/AffiliateQueryDTO.java | 13 ++
.../dto/GenerateThroughImageTextDTO.java | 31 +--
.../com/ai/da/model/dto/TimeQueryBaseDTO.java | 19 ++
.../java/com/ai/da/model/vo/AffiliateVO.java | 15 ++
.../com/ai/da/service/AffiliateService.java | 22 ++
.../java/com/ai/da/service/StripeService.java | 4 +
.../da/service/impl/AffiliateServiceImpl.java | 189 ++++++++++++++++++
.../impl/CollectionElementServiceImpl.java | 4 +-
.../service/impl/PaymentInfoServiceImpl.java | 12 +-
.../ai/da/service/impl/StripeServiceImpl.java | 28 ++-
21 files changed, 561 insertions(+), 57 deletions(-)
create mode 100644 src/main/java/com/ai/da/controller/AffiliateController.java
create mode 100644 src/main/java/com/ai/da/mapper/primary/AffiliateMapper.java
create mode 100644 src/main/java/com/ai/da/mapper/primary/entity/Affiliate.java
create mode 100644 src/main/java/com/ai/da/model/dto/AffiliateEmailParamsDTO.java
create mode 100644 src/main/java/com/ai/da/model/dto/AffiliateQueryDTO.java
create mode 100644 src/main/java/com/ai/da/model/dto/TimeQueryBaseDTO.java
create mode 100644 src/main/java/com/ai/da/model/vo/AffiliateVO.java
create mode 100644 src/main/java/com/ai/da/service/AffiliateService.java
create mode 100644 src/main/java/com/ai/da/service/impl/AffiliateServiceImpl.java
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;
+ }
+
+
+
}
From 5dd1e10b613cb7d071812d730aac012823fb0abb Mon Sep 17 00:00:00 2001
From: xupei
Date: Tue, 10 Dec 2024 10:09:21 +0800
Subject: [PATCH 10/91] to dev
---
.../java/com/ai/da/common/config/MyTaskScheduler.java | 4 ++--
src/main/java/com/ai/da/common/task/AccountTask.java | 10 +++++-----
src/main/java/com/ai/da/common/task/PaypalTask.java | 2 +-
src/main/java/com/ai/da/common/task/StripeTask.java | 2 +-
src/main/resources/application.properties | 4 ++--
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java
index 95c55e9a..61e964ea 100644
--- a/src/main/java/com/ai/da/common/config/MyTaskScheduler.java
+++ b/src/main/java/com/ai/da/common/config/MyTaskScheduler.java
@@ -51,7 +51,7 @@ public class MyTaskScheduler {
// 定时任务,每十五天执行一次
// @Scheduled(cron = "0 0 0 ? * MON")
- @Scheduled(cron = "0 0 0 */15 * ?")
+// @Scheduled(cron = "0 0 0 */15 * ?")
public void checkExpiry() {
// 检测正式用户是否快要过期
QueryWrapper qw = new QueryWrapper<>();
@@ -85,7 +85,7 @@ public class MyTaskScheduler {
}
}
}
- @Scheduled(cron = "0 0 9 * * ?")
+// @Scheduled(cron = "0 0 9 * * ?")
public void sendTrialOrderExcelToManagements() {
// 获取前一天日期
LocalDate yesterday = LocalDate.now().minusDays(1);
diff --git a/src/main/java/com/ai/da/common/task/AccountTask.java b/src/main/java/com/ai/da/common/task/AccountTask.java
index 6950878d..cfc65fd4 100644
--- a/src/main/java/com/ai/da/common/task/AccountTask.java
+++ b/src/main/java/com/ai/da/common/task/AccountTask.java
@@ -19,21 +19,21 @@ public class AccountTask {
/**
* 每周日晚上刷新 年付用户、月付用户的积分
*/
- @Scheduled(cron = "59 59 23 ? * SUN")
+// @Scheduled(cron = "59 59 23 ? * SUN")
// @Scheduled(cron = "59 59 23 * * ?")
public void refreshCreditsMonthly() {
log.info("每周日晚11:59:59刷新付费用户积分为 6000");
accountService.refreshCreditsWeekly();
}
- @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
+// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void getPaidUser() {
// 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单
accountService.extendValidityForCC();
}
// 每天凌晨0点执行一次
- @Scheduled(cron = "0 0 0 * * ?")
+// @Scheduled(cron = "0 0 0 * * ?")
public void cancelActivityBenefits() {
// 1、查询当前所有参与了活动且过期的用户
List accountList = accountService.getExpiredUserBySystemUser(4);
@@ -46,7 +46,7 @@ public class AccountTask {
}
// 每天检测正式用户到期情况,每天凌晨0点执行
- @Scheduled(cron = "0 0 0 * * ?")
+// @Scheduled(cron = "0 0 0 * * ?")
public void paidUserToVisitor() {
// 1、查询当前已过期正式用户或试用用户
List accountList = accountService.getExpiredUserBySystemUser(1);
@@ -63,7 +63,7 @@ public class AccountTask {
/**
* 将Code-Create上注册的用户添加为AiDA的游客
*/
- @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
+// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
public void registerUserToVisitor() {
accountService.registerUserToVisitor();
}
diff --git a/src/main/java/com/ai/da/common/task/PaypalTask.java b/src/main/java/com/ai/da/common/task/PaypalTask.java
index 865eaaa4..fa069971 100644
--- a/src/main/java/com/ai/da/common/task/PaypalTask.java
+++ b/src/main/java/com/ai/da/common/task/PaypalTask.java
@@ -23,7 +23,7 @@ public class PaypalTask {
@Resource
private PayPalCheckoutService payPalCheckoutService;
- @Scheduled(cron = "0/30 * * * * ?")
+// @Scheduled(cron = "0/30 * * * * ?")
public void orderConfirm() throws SerializeException {
// log.info("PayPal orderConfirm 被执行......");
diff --git a/src/main/java/com/ai/da/common/task/StripeTask.java b/src/main/java/com/ai/da/common/task/StripeTask.java
index 45d98997..d4a6fbaf 100644
--- a/src/main/java/com/ai/da/common/task/StripeTask.java
+++ b/src/main/java/com/ai/da/common/task/StripeTask.java
@@ -22,7 +22,7 @@ public class StripeTask {
@Resource
private StripeService stripeService;
- @Scheduled(cron = "0/30 * * * * ?")
+// @Scheduled(cron = "0/30 * * * * ?")
public void orderConfirm() throws SerializeException {
// 查看超过30分钟以上仍未支付的订单 置为超时订单
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8550475d..974fee23 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -2,7 +2,7 @@
#spring.profiles.active=test
#����application-prod�ļ�(��������)
-spring.profiles.active=prod
+#spring.profiles.active=prod
#����application-dev�ļ�(��������)
-#spring.profiles.active=dev
+spring.profiles.active=dev
From 2ac54a50ecb91c846cf6d6b3fe575778fe82b960 Mon Sep 17 00:00:00 2001
From: shahaibo <1023316923@qq.com>
Date: Wed, 11 Dec 2024 14:21:29 +0800
Subject: [PATCH 11/91] TASK:AiDA
---
.../ai/da/controller/AccountController.java | 14 +++++++++++
.../da/model/dto/ReDesignCollectionDTO.java | 4 ++++
.../com/ai/da/service/AccountService.java | 4 ++++
.../da/service/impl/AccountServiceImpl.java | 23 +++++++++++++++++++
.../ai/da/service/impl/DesignServiceImpl.java | 4 ++--
5 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java
index 1a8485d7..219d890f 100644
--- a/src/main/java/com/ai/da/controller/AccountController.java
+++ b/src/main/java/com/ai/da/controller/AccountController.java
@@ -310,4 +310,18 @@ public class AccountController {
public Response getAccountDetail() {
return Response.success(accountService.getAccountDetail());
}
+
+ @CrossOrigin
+ @GetMapping("/bindGoogle")
+ @ApiOperation(value = "绑定谷歌")
+ public Response bindGoogle(@RequestParam("credential") String credential) {
+ return Response.success(accountService.bindGoogle(credential));
+ }
+
+ @CrossOrigin
+ @GetMapping("/bindWeChat")
+ @ApiOperation(value = "绑定微信")
+ public Response bindWeChat(@RequestParam("code") String code) {
+ return Response.success(accountService.bindWeChat(code));
+ }
}
diff --git a/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java b/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
index 65a125a9..8c8bbe48 100644
--- a/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
+++ b/src/main/java/com/ai/da/model/dto/ReDesignCollectionDTO.java
@@ -67,4 +67,8 @@ public class ReDesignCollectionDTO {
private String moodboardPosition;
private String moodTemplateId;
+
+ private List requestIdList;
+
+ private Integer designNum;
}
diff --git a/src/main/java/com/ai/da/service/AccountService.java b/src/main/java/com/ai/da/service/AccountService.java
index 262f3b91..6a5fd436 100644
--- a/src/main/java/com/ai/da/service/AccountService.java
+++ b/src/main/java/com/ai/da/service/AccountService.java
@@ -208,4 +208,8 @@ public interface AccountService extends IService {
AccountLoginVO parseWeChatCode(String code);
AccountLoginVO getAccountDetail();
+
+ AccountLoginVO bindGoogle(String credential);
+
+ AccountLoginVO bindWeChat(String code);
}
diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
index 74483e51..20fff9b4 100644
--- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
@@ -1503,6 +1503,16 @@ public class AccountServiceImpl extends ServiceImpl impl
}
}
+ public static void main(String[] args) {
+ List a = new ArrayList<>();
+ a.add("1023316923@qq.com");
+ a.add("Malinyuquan@gmail.com");
+
+ if (a.contains("malinyuquan@gmail.com")) {
+ log.info("aaaaaaaaaaaaaaaaaaaaa");
+ }
+ }
+
private static final String QUERY_PAID_CUSTOMER_EMAIL = "SELECT distinct c.email " +
"FROM `pmr_wc_order_stats` o " +
"inner join `pmr_wc_customer_lookup` c " +
@@ -2400,8 +2410,21 @@ public class AccountServiceImpl extends ServiceImpl impl
response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
+ response.setSystemUser(1);
//判断是否常用ip 不是则发邮件提示
// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
+
+
return response;
}
+
+ @Override
+ public AccountLoginVO bindGoogle(String credential) {
+ return null;
+ }
+
+ @Override
+ public AccountLoginVO bindWeChat(String code) {
+ return null;
+ }
}
diff --git a/src/main/java/com/ai/da/service/impl/DesignServiceImpl.java b/src/main/java/com/ai/da/service/impl/DesignServiceImpl.java
index 3abb7e36..e26071a3 100644
--- a/src/main/java/com/ai/da/service/impl/DesignServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/DesignServiceImpl.java
@@ -1562,8 +1562,8 @@ public class DesignServiceImpl extends ServiceImpl impleme
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean receiveDesignResults(JSONObject responseJSONObject) {
- String requestId = "UUID.randomUUID().toString()";
-// String requestId = responseJSONObject.getString("requestId");
+// String requestId = "UUID.randomUUID().toString()";
+ String requestId = responseJSONObject.getString("requestId");
Map context;
synchronized (designContext) {
log.info(designContext.toString());
From 82f75716124abf2d7cb9bd8178b992b4767ea0f5 Mon Sep 17 00:00:00 2001
From: shahaibo <1023316923@qq.com>
Date: Wed, 11 Dec 2024 16:21:18 +0800
Subject: [PATCH 12/91] TASK:AiDA
---
.../ai/da/controller/AccountController.java | 4 +-
.../mapper/primary/entity/AccountExtend.java | 5 +
.../com/ai/da/model/vo/AccountLoginVO.java | 4 +
.../com/ai/da/service/AccountService.java | 4 +-
.../da/service/impl/AccountServiceImpl.java | 116 ++++++++++++++----
5 files changed, 107 insertions(+), 26 deletions(-)
diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java
index 219d890f..5ec9754a 100644
--- a/src/main/java/com/ai/da/controller/AccountController.java
+++ b/src/main/java/com/ai/da/controller/AccountController.java
@@ -314,14 +314,14 @@ public class AccountController {
@CrossOrigin
@GetMapping("/bindGoogle")
@ApiOperation(value = "绑定谷歌")
- public Response bindGoogle(@RequestParam("credential") String credential) {
+ public Response bindGoogle(@RequestParam("credential") String credential) {
return Response.success(accountService.bindGoogle(credential));
}
@CrossOrigin
@GetMapping("/bindWeChat")
@ApiOperation(value = "绑定微信")
- public Response bindWeChat(@RequestParam("code") String code) {
+ public Response bindWeChat(@RequestParam("code") String code) {
return Response.success(accountService.bindWeChat(code));
}
}
diff --git a/src/main/java/com/ai/da/mapper/primary/entity/AccountExtend.java b/src/main/java/com/ai/da/mapper/primary/entity/AccountExtend.java
index 5742f0ec..c5b612b1 100644
--- a/src/main/java/com/ai/da/mapper/primary/entity/AccountExtend.java
+++ b/src/main/java/com/ai/da/mapper/primary/entity/AccountExtend.java
@@ -1,5 +1,6 @@
package com.ai.da.mapper.primary.entity;
+import com.ai.da.common.response.PageResponse;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -26,5 +27,9 @@ public class AccountExtend implements Serializable {
private String authType;
+ private String headImgUrl;
+
+ private String name;
+
private String auth;
}
diff --git a/src/main/java/com/ai/da/model/vo/AccountLoginVO.java b/src/main/java/com/ai/da/model/vo/AccountLoginVO.java
index ab23b872..9a1f9b85 100644
--- a/src/main/java/com/ai/da/model/vo/AccountLoginVO.java
+++ b/src/main/java/com/ai/da/model/vo/AccountLoginVO.java
@@ -1,5 +1,6 @@
package com.ai.da.model.vo;
+import com.ai.da.mapper.primary.entity.AccountExtend;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.models.auth.In;
@@ -8,6 +9,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
+import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@@ -40,4 +42,6 @@ public class AccountLoginVO {
private Long followerCount;
+ private List accountExtendList;
+
}
diff --git a/src/main/java/com/ai/da/service/AccountService.java b/src/main/java/com/ai/da/service/AccountService.java
index 6a5fd436..2f2f6ff3 100644
--- a/src/main/java/com/ai/da/service/AccountService.java
+++ b/src/main/java/com/ai/da/service/AccountService.java
@@ -209,7 +209,7 @@ public interface AccountService extends IService {
AccountLoginVO getAccountDetail();
- AccountLoginVO bindGoogle(String credential);
+ Boolean bindGoogle(String credential);
- AccountLoginVO bindWeChat(String code);
+ Boolean bindWeChat(String code);
}
diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
index 20fff9b4..7fff9732 100644
--- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
+++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java
@@ -2179,6 +2179,7 @@ public class AccountServiceImpl extends ServiceImpl impl
String userId = payload.getSubject();
String email = payload.getEmail();
String name = (String) payload.get("name");
+ String pictureUrl = (String) payload.get("picture");
log.info(userId);
log.info(email);
log.info(name);
@@ -2191,6 +2192,13 @@ public class AccountServiceImpl extends ServiceImpl impl
if (CollectionUtil.isNotEmpty(accounts)) {
account = accounts.get(0);
} else {
+
+ AccountExtend accountExtendInsert = new AccountExtend();
+ accountExtendInsert.setAuth(userId);
+ accountExtendInsert.setAuthType("Google");
+ accountExtendInsert.setHeadImgUrl(pictureUrl);
+ accountExtendInsert.setName(name);
+
// 用户不存在,创建新用户(自动注册)
Account newUser = new Account();
newUser.setUserEmail(email);
@@ -2206,6 +2214,9 @@ public class AccountServiceImpl extends ServiceImpl impl
newUser.setSystemUser(3);
accountMapper.insert(newUser);
+ accountExtendInsert.setAccountId(newUser.getId());
+ accountExtendMapper.insert(accountExtendInsert);
+
account = newUser;
}
@@ -2264,6 +2275,7 @@ public class AccountServiceImpl extends ServiceImpl impl
// 提取 unionid 和 nickname
String unionId = userInfoResponse.getString("unionid");
String userName = userInfoResponse.getString("nickname");
+ String headimgurl = userInfoResponse.getString("headimgurl");
if (unionId == null) {
throw new IllegalArgumentException("无法获取 unionid,请检查微信开发平台配置");
}
@@ -2278,6 +2290,8 @@ public class AccountServiceImpl extends ServiceImpl impl
AccountExtend accountExtendInsert = new AccountExtend();
accountExtendInsert.setAuth(unionId);
accountExtendInsert.setAuthType("WeChat");
+ accountExtendInsert.setHeadImgUrl(headimgurl);
+ accountExtendInsert.setName(userName);
// 用户不存在,创建新用户(自动注册)
Account newUser = new Account();
@@ -2325,21 +2339,6 @@ public class AccountServiceImpl extends ServiceImpl impl
response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
return response;
-
-// // 2. 根据 unionid 检查用户是否存在
-// User user = userRepository.findByUnionid(unionid);
-// if (user == null) {
-// // 用户不存在,进行注册
-// user = new User();
-// user.setUnionid(unionid);
-// user.setOpenid(weChatResponse.getString("openid"));
-// user.setNickname(weChatResponse.getString("nickname"));
-// user.setAvatar(weChatResponse.getString("headimgurl"));
-// userRepository.save(user);
-// }
-//
-// // 3. 返回 unionid
-// return unionid;
}
private static final String WECHAT_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";
@@ -2410,21 +2409,94 @@ public class AccountServiceImpl extends ServiceImpl impl
response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
- response.setSystemUser(1);
- //判断是否常用ip 不是则发邮件提示
-// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
+ QueryWrapper