diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java index 775b35b0..bbddb359 100644 --- a/src/main/java/com/ai/da/controller/AccountController.java +++ b/src/main/java/com/ai/da/controller/AccountController.java @@ -289,7 +289,7 @@ public class AccountController { @PostMapping("organizationNameSearch") @ApiOperation(value = "组织名模糊查询") - public Response> organizationNameSearch(@RequestParam("type") String type, @RequestParam("name") String name) { + public Response> organizationNameSearch(@RequestParam("type") String type, @RequestParam(value = "name", required = false) String name) { return Response.success(accountService.organizationNameSearch(type, name)); } diff --git a/src/main/java/com/ai/da/controller/SubscriptionPlanController.java b/src/main/java/com/ai/da/controller/SubscriptionPlanController.java index 6619de92..b56298cf 100644 --- a/src/main/java/com/ai/da/controller/SubscriptionPlanController.java +++ b/src/main/java/com/ai/da/controller/SubscriptionPlanController.java @@ -58,5 +58,19 @@ public class SubscriptionPlanController { return Response.success(); } + @ApiOperation("activeSubscriptionPlan") + @GetMapping("/activeSubscriptionPlan") + public Response activeSubscriptionPlan() { + subscriptionPlanService.activeSubscriptionPlan(); + return Response.success(); + } + + @ApiOperation("expireSubscription") + @GetMapping("/expireSubscription") + public Response expireSubscription() { + subscriptionPlanService.expireSubscription(); + return Response.success(); + } + } diff --git a/src/main/java/com/ai/da/service/SubscriptionPlanService.java b/src/main/java/com/ai/da/service/SubscriptionPlanService.java index e059b6a6..c41fe067 100644 --- a/src/main/java/com/ai/da/service/SubscriptionPlanService.java +++ b/src/main/java/com/ai/da/service/SubscriptionPlanService.java @@ -19,4 +19,8 @@ public interface SubscriptionPlanService extends IService { void deletePlan(Long id); void switchSubscriptionPlan(Long subscriptionPlanId, Long adminAccId); + + void activeSubscriptionPlan(); + + void expireSubscription(); } 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 503fbdc7..ee9515b1 100644 --- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java @@ -3593,7 +3593,7 @@ public class AccountServiceImpl extends ServiceImpl impl @Override public Set organizationNameSearch(String type, String name) { - QueryWrapper qw = new QueryWrapper<>(); + /*QueryWrapper qw = new QueryWrapper<>(); qw.lambda().ne(Account::getOrganizationName, "").isNotNull(Account::getOrganizationName); if (!StringUtil.isNullOrEmpty(name)) { qw.lambda().like(Account::getOrganizationName, name); @@ -3615,6 +3615,20 @@ public class AccountServiceImpl extends ServiceImpl impl if (CollectionUtil.isNotEmpty(accountList)) { return accountList.stream().map(Account::getOrganizationName).collect(Collectors.toSet()); } + return new HashSet<>();*/ + + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotBlank(type)){ + type = type.equals("Enterprise") ? "Enterprise" : "Education"; + queryWrapper.lambda().eq(Organization::getType, type); + } + if (StringUtils.isNotBlank(name)) { + queryWrapper.lambda().like(Organization::getName, name); + } + List organizations = organizationMapper.selectList(queryWrapper); + if (CollectionUtil.isNotEmpty(organizations)) { + return organizations.stream().map(Organization::getName).collect(Collectors.toSet()); + } return new HashSet<>(); } 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 737cbc0c..17c4596b 100644 --- a/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/StripeServiceImpl.java @@ -1101,9 +1101,9 @@ public class StripeServiceImpl implements StripeService { setSubscriptionParams(paymentInfo, subscriptionInfo, orderByOrderNo, emailParamsDTO, language); log.info("SEND EMAIL: type={}, params={}, language={}, receiver={}", type, JSONObject.toJSON(emailParamsDTO), language, account.getUserEmail()); -// boolean b = SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail()); + boolean b = SendEmailUtil.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail()); // boolean b = emailService.subscriptionEmailReminder(type, emailParamsDTO, language, account.getUserEmail()); -// if (!b) return false; + if (!b) return false; // 邮件通知成功后,更新标志 if (!type.startsWith("reminder") && !type.equals("cancel")){ diff --git a/src/main/java/com/ai/da/service/impl/SubscriptionPlanServiceImpl.java b/src/main/java/com/ai/da/service/impl/SubscriptionPlanServiceImpl.java index 012151ca..020510cd 100644 --- a/src/main/java/com/ai/da/service/impl/SubscriptionPlanServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/SubscriptionPlanServiceImpl.java @@ -2,6 +2,7 @@ package com.ai.da.service.impl; import com.ai.da.common.config.exception.BusinessException; 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.RedisUtil; import com.ai.da.mapper.primary.AccountMapper; @@ -51,7 +52,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl resultPage = baseMapper.selectPage(page, queryWrapper); // 4. 转换为VO并返回 - return resultPage.convert((Function) plan -> CopyUtil.copyObject(plan, SubscriptionPlanVO.class)) ; + return resultPage.convert((Function) plan -> CopyUtil.copyObject(plan, SubscriptionPlanVO.class)); } /** @@ -129,10 +130,10 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl 100) { - throw new BusinessException("每页数量必须在1-100之间"); + throw new BusinessException("page.size.limit"); } // 时间格式校验 @@ -142,10 +143,10 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl 0) { - throw new BusinessException("存在" + activeSubAcc + "个用户使用此计划,无法删除"); + throw new BusinessException("users.currently.using.this.plan"); } // 检查是否在有效期内 if (plan.getCurrentPeriodEnd() != null && isExpired(plan.getCurrentPeriodEnd())) { - throw new BusinessException("计划仍在有效期内,请到期后再删除"); + throw new BusinessException("valid.subscription.period"); } } @@ -321,6 +323,10 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl 0) { + updateAccount.setCredits(plan.getCreditLimit().add(leftCredits)); + } else { + updateAccount.setCredits(plan.getCreditLimit()); + } } else { updateAccount.setCredits(plan.getCreditLimit().add(account.getCredits())); } @@ -422,7 +431,7 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl 0) { log.info("管理员账号更新成功,ID: {}, 有效期至: {}, 用户类型: {}, 积分: {}", account.getId(), - formatTimestamp(newValidEndTime), + formatTimestamp(plan.getCurrentPeriodEnd()), newSystemUser, plan.getCreditLimit()); @@ -461,7 +470,8 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl recentlyExpiredPlans = findRecentlyExpiredPlans(); + if (CollectionUtils.isEmpty(recentlyExpiredPlans)) { + log.info("过去24h没有过期的订阅计划"); + return; + } + + log.info("发现{}个过去24h过期的订阅计划", recentlyExpiredPlans.size()); + // 2. 更新与订阅相关账号的状态 for (SubscriptionPlan subscriptionPlan : recentlyExpiredPlans) { processExpiringSubscriptionPlan(subscriptionPlan); + // 6. 修改订阅状态 + subscriptionPlan.setStatus(SubscriptionPlan.SubscriptionStatus.EXPIRED.name()); + subscriptionPlan.setUpdateTime(LocalDateTime.now()); + baseMapper.updateById(subscriptionPlan); } } @@ -538,6 +559,8 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("is_deleted", 0) .between("current_period_end", yesterday, now) // 过去24小时内到期 @@ -577,9 +600,6 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl now || plan.getCurrentPeriodEnd() < now) { - return null; // 不在有效期内 - } - return plan; } @@ -623,6 +638,11 @@ public class SubscriptionPlanServiceImpl extends ServiceImpl update t_account set is_trial = 0, credits = 0, system_user = 0, - organization_name = null, credits_usage = null, credits_usage_limit = null, sub_account_num = null + organization_name = null, organization_id = null, credits_usage = null, credits_usage_limit = null, sub_account_num = null, + parent_id = null, subscription_plan_id = null where id = #{id} diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 68e47a9c..a6d53e54 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -190,6 +190,21 @@ unknow.affiliate=Unknown affiliate id. unknown.operationType=Unknown operationType. unknown.mode=unknown mode unknown.subscription.plan=unknown subscription plan +unknown.subscription.status=Unknown subscription status. +subscription.has.expired=Switch failed. The subscription has expired. +unknown.administrator.user=Switch failed. Unknown administrator user. +no.permission.manage.subscription=Switch failed. You do not have permission to manage this subscription. +unknown.organization=Unknown organization. +valid.subscription.period=The plan is still within its valid period. Please delete it after it expires. +users.currently.using.this.plan=There are users currently using this plan, so it cannot be deleted. +deletion.failed.please.try.again.later=Deletion failed. Please try again later. +subscription.plan.has.been.deleted=This subscription plan has been deleted. +subscription.plan.does.not.exist=The subscription plan does not exist. +ID.cannot.be.empty.and.must.be.greater.than.0=ID cannot be empty and must be greater than 0. +invalid.time.format=Invalid time format. Please use the yyyy-MM-dd HH:mm:ss format. +the.start.time.cannot.be.later.than.the.end.time=The start time cannot be later than the end time. +page.size.limit=The number of items per page must be between 1 and 100. +page.num.limit=The page number must be greater than 0. # 可能会报异常 # Informative: diff --git a/src/main/resources/messages_zh.properties b/src/main/resources/messages_zh.properties index 32bb3c1e..d5c5985f 100644 --- a/src/main/resources/messages_zh.properties +++ b/src/main/resources/messages_zh.properties @@ -186,6 +186,21 @@ unknow.affiliate=未知推广者id unknown.operationType=未知操作类型 unknown.mode=未知模式 unknown.subscription.plan=未知订阅计划 +unknown.subscription.status=未知订阅状态 +subscription.has.expired=切换失败,订阅已过期 +unknown.administrator.user=切换失败,未知管理员用户 +no.permission.manage.subscription=切换失败,您没有权限管理该订阅 +unknown.organization=未知组织 +valid.subscription.period=计划仍在有效期内,请到期后再删除 +users.currently.using.this.plan=存在用户正在使用此计划,无法删除 +deletion.failed.please.try.again.later=删除失败,请稍后重试 +subscription.plan.has.been.deleted=该订阅计划已被删除 +subscription.plan.does.not.exist=订阅计划不存在 +ID.cannot.be.empty.and.must.be.greater.than.0=ID不能为空且必须大于0 +invalid.time.format=时间格式错误,请使用yyyy-MM-dd HH:mm:ss格式 +the.start.time.cannot.be.later.than.the.end.time=开始时间不能晚于结束时间 +page.size.limit=每页数量必须在1-100之间 +page.num.limit=页码必须大于0 # 可能会报异常 # Informative: diff --git a/src/main/resources/payment.properties b/src/main/resources/payment.properties index 0f2800d6..bc57019c 100644 --- a/src/main/resources/payment.properties +++ b/src/main/resources/payment.properties @@ -27,20 +27,20 @@ paypal.webhook_id=1D107312EX592781K ##### Stripe # developer -stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 +#stripe.private-key=sk_test_51P4ZZL02n1TEydyN8qQHjOA9imsFU7Oxs2HMHGy2urHnnQgSHnZuu5vVP6pKhEACwUpsKNyrbZpdcg5TJWJLRHcY008dEO1fn2 # dev 端点 -stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w +#stripe.webhook-sign-secret=whsec_e0dBiJngx6qqgJj6yPyJ2A9ouh1Cjv5w # local 端点 #stripe.webhook-sign-secret=whsec_TJcMSnAkh4uktrNY1M6Iy8XaVze4Rzqm # kim - test -#stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0 +stripe.private-key=sk_test_51LwPrxH7nPZ8bkrNj67TFD7sxucaTANs1lf0KGSu1QSJfxYXcnigq2wTaZyZzST7y0fMbhhvaJZ4LjjFhr95M83a00eXrmOTL0 # prod 端点 #stripe.webhook-sign-secret=whsec_GoyVEAaBtuGD5Rt55z83JnPnLDAZTN3u # local 端点 #stripe.webhook-sign-secret=whsec_NvwM3hDQiN5GXclYOYekE9IKHLjmROF8 # dev 端点 -#stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL +stripe.webhook-sign-secret=whsec_pX0pPMQm85PaUSWnFMEzoccb3MGNkjoL # kim - live #stripe.private-key=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