Merge branch 'dev/dev_xp' into dev/3.1_release_merge
# Conflicts: # src/main/java/com/ai/da/controller/AccountController.java
This commit is contained in:
@@ -88,7 +88,7 @@ public class SendRequestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public String sendFluxPost(String url, String requestBodyStr){
|
||||
/*public String sendFluxPost(String url, String requestBodyStr){
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
@@ -103,9 +103,63 @@ public class SendRequestUtil {
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
if (status == 402 || status == 403) {
|
||||
SendEmailUtil.commonExceptionReminder("Flux账户积分不足,flux生成任务",
|
||||
new String[]{"xupei3360@163.com, fangjianliao@aidlab.hk, investigation@aidlab.hk"});
|
||||
}
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}", status);
|
||||
return null;
|
||||
}*/
|
||||
|
||||
public String sendFluxPost(String url, String requestBodyStr) {
|
||||
// 尝试两个API key
|
||||
String[] apiKeys = {"d447a0ac-2291-4f1c-9a36-f7614c385989",
|
||||
"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7"};
|
||||
boolean[] notified = {false, false}; // 记录是否已发送过不足提醒
|
||||
|
||||
for (int i = 0; i < apiKeys.length; i++) {
|
||||
int status;
|
||||
String body;
|
||||
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header("x-key", apiKeys[i])
|
||||
.body(requestBodyStr)
|
||||
.timeout(180000)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// 余额不足处理
|
||||
if (status == 402 || status == 403) {
|
||||
if (!notified[i]) {
|
||||
SendEmailUtil.commonExceptionReminder(
|
||||
"Flux账户积分不足,flux生成任务失败",
|
||||
new String[]{"xupei3360@163.com, fangjianliao@aidlab.hk, investigation@aidlab.hk"}
|
||||
);
|
||||
notified[i] = true;
|
||||
}
|
||||
continue; // 尝试下一个key
|
||||
}
|
||||
|
||||
// 其他错误直接返回null
|
||||
log.warn("请求失败,状态码为:{},使用key:{}", status, apiKeys[i]);
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("请求异常,使用key:{}", apiKeys[i], e);
|
||||
if (i == apiKeys.length - 1) return null; // 最后一个key也失败则返回null
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("所有API key均余额不足");
|
||||
return null;
|
||||
}
|
||||
|
||||
public String sendPost(String url, String requestBodyStr){
|
||||
|
||||
@@ -289,7 +289,7 @@ public class AccountController {
|
||||
|
||||
@PostMapping("organizationNameSearch")
|
||||
@Operation(summary = "组织名模糊查询")
|
||||
public Response<Set<String>> organizationNameSearch(@RequestParam("type") String type, @RequestParam("name") String name) {
|
||||
public Response<Set<String>> organizationNameSearch(@RequestParam("type") String type, @RequestParam(value = "name", required = false) String name) {
|
||||
return Response.success(accountService.organizationNameSearch(type, name));
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public class StripeController {
|
||||
Long size = redisUtil.getSize(key_1);
|
||||
// 给我发送邮件
|
||||
if (webhookReminderFlag.equals("1") && size == 3){
|
||||
SendEmailUtil.commonExceptionReminder("Stripe Webhook 回调", new String[]{"xupei3360@163.com"});
|
||||
SendEmailUtil.commonExceptionReminder("Stripe Webhook 回调处理出现异常", new String[]{"xupei3360@163.com"});
|
||||
}
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.SubscriptionPlanDTO;
|
||||
import com.ai.da.model.dto.SubscriptionPlanPageQuery;
|
||||
import com.ai.da.model.dto.UpdateSubscriptionPlanDTO;
|
||||
import com.ai.da.model.vo.SubscriptionPlanVO;
|
||||
import com.ai.da.service.SubscriptionPlanService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Api(tags = "订阅计划模块")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/subscription_plan")
|
||||
public class SubscriptionPlanController {
|
||||
|
||||
private final SubscriptionPlanService subscriptionPlanService;
|
||||
|
||||
@ApiOperation("创建订阅计划")
|
||||
@PostMapping("/createPlan")
|
||||
public Response<String> createPlan(@Valid @RequestBody SubscriptionPlanDTO subscriptionPlanDTO) {
|
||||
subscriptionPlanService.createPlan(subscriptionPlanDTO);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation("更新订阅计划")
|
||||
@PostMapping("/updatePlan")
|
||||
public Response<String> updatePlan(@Valid @RequestBody UpdateSubscriptionPlanDTO updateDTO) {
|
||||
subscriptionPlanService.updatePlan(updateDTO);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation("搜索订阅计划")
|
||||
@PostMapping("/searchByPage")
|
||||
public Response<IPage<SubscriptionPlanVO>> searchByPage(@Valid @RequestBody SubscriptionPlanPageQuery subscriptionPlanPageQuery) {
|
||||
IPage<SubscriptionPlanVO> subscriptionPlanVOIPage = subscriptionPlanService.searchByPage(subscriptionPlanPageQuery);
|
||||
return Response.success(subscriptionPlanVOIPage);
|
||||
}
|
||||
|
||||
@ApiOperation("删除订阅计划")
|
||||
@GetMapping("/deletePlan")
|
||||
public Response<String> deletePlan(@RequestParam Long id) {
|
||||
subscriptionPlanService.deletePlan(id);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation("管理员切换订阅计划")
|
||||
@GetMapping("/switchSubscriptionPlan")
|
||||
public Response<String> switchSubscriptionPlan(@RequestParam Long subscriptionPlanId, @RequestParam Long adminAccId) {
|
||||
subscriptionPlanService.switchSubscriptionPlan(subscriptionPlanId, adminAccId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation("activeSubscriptionPlan")
|
||||
@GetMapping("/activeSubscriptionPlan")
|
||||
public Response<String> activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation("expireSubscription")
|
||||
@GetMapping("/expireSubscription")
|
||||
public Response<String> expireSubscription() {
|
||||
subscriptionPlanService.expireSubscription();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ai.da.mapper.primary;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.SubscriptionPlan;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
public interface SubscriptionPlanMapper extends BaseMapper<SubscriptionPlan> {
|
||||
}
|
||||
@@ -140,4 +140,6 @@ public class Account implements Serializable {
|
||||
|
||||
@ApiModelProperty("givenName")
|
||||
private String givenName;
|
||||
|
||||
private Long subscriptionPlanId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.ai.da.mapper.primary.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@TableName("t_subscription_plan")
|
||||
public class SubscriptionPlan extends BaseEntity{
|
||||
/**
|
||||
* 组织id
|
||||
*/
|
||||
private Long organizationId;
|
||||
|
||||
/**
|
||||
* 订阅命名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 当前订阅开始时间
|
||||
*/
|
||||
private Long currentPeriodStart;
|
||||
|
||||
/**
|
||||
* 当前订阅结束时间
|
||||
*/
|
||||
private Long currentPeriodEnd;
|
||||
|
||||
/**
|
||||
* 当前订阅总的子账号数量
|
||||
*/
|
||||
private Integer accountNum;
|
||||
|
||||
/**
|
||||
* 当前订阅可用积分上限
|
||||
*/
|
||||
private BigDecimal creditLimit;
|
||||
|
||||
/**
|
||||
* 当前订阅已使用积分
|
||||
*/
|
||||
private BigDecimal creditUsage;
|
||||
|
||||
/**
|
||||
* 管理员账户id
|
||||
*/
|
||||
private Long adminAccId;
|
||||
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private Integer isDeleted;
|
||||
|
||||
/**
|
||||
* 删除人的用户id
|
||||
*/
|
||||
private Long deleteBy;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
// 在类内部定义的枚举
|
||||
@Getter
|
||||
public enum SubscriptionStatus {
|
||||
PENDING("待激活", 0),
|
||||
ACTIVE("已激活", 1),
|
||||
EXPIRED("已过期", 2),
|
||||
CANCELLED("已取消", 3);
|
||||
|
||||
private final String desc;
|
||||
private final int code;
|
||||
|
||||
SubscriptionStatus(String desc, int code) {
|
||||
this.desc = desc;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
39
src/main/java/com/ai/da/model/dto/SubscriptionPlanDTO.java
Normal file
39
src/main/java/com/ai/da/model/dto/SubscriptionPlanDTO.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@ApiModel( value = "创建订阅计划入参")
|
||||
public class SubscriptionPlanDTO {
|
||||
|
||||
@ApiModelProperty("组织id")
|
||||
@NotNull(message = "Please select an organizationId.")
|
||||
private Long organizationId;
|
||||
|
||||
@ApiModelProperty("当前订阅开始时间")
|
||||
@NotNull(message = "Please set a subscription start time.")
|
||||
private Long currentPeriodStart;
|
||||
|
||||
@ApiModelProperty("当前订阅结束时间")
|
||||
@NotNull(message = "Please set a subscription end time.")
|
||||
private Long currentPeriodEnd;
|
||||
|
||||
@ApiModelProperty("当前订阅总的子账号数量")
|
||||
@NotNull(message = "Please set the sub-account number.")
|
||||
private Integer accountNum;
|
||||
|
||||
@ApiModelProperty("当前订阅可用积分上限")
|
||||
@NotNull(message = "Please set the credits limit.")
|
||||
private BigDecimal creditLimit;
|
||||
|
||||
@ApiModelProperty("管理员账户id")
|
||||
@NotNull(message = "Please assign an administrator account.")
|
||||
private Long adminAccId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ApiModel
|
||||
public class SubscriptionPlanPageQuery extends QueryPageByTimeDTO {
|
||||
|
||||
@ApiModelProperty("组织id")
|
||||
private Long organizationId;
|
||||
|
||||
@ApiModelProperty("管理id")
|
||||
private Long adminAccId;
|
||||
|
||||
@ApiModelProperty("状态 PENDING||ACTIVE||EXPIRED")
|
||||
private List<String> status;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ai.da.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@ApiModel
|
||||
public class UpdateSubscriptionPlanDTO {
|
||||
|
||||
@ApiModelProperty("id")
|
||||
@NotNull(message = "subscription plan id cannot be empty")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("当前订阅开始时间")
|
||||
private Long currentPeriodStart;
|
||||
|
||||
@ApiModelProperty("当前订阅结束时间")
|
||||
private Long currentPeriodEnd;
|
||||
|
||||
@ApiModelProperty("当前订阅总的子账号数量")
|
||||
private Integer accountNum;
|
||||
|
||||
@ApiModelProperty("当前订阅可用积分上限")
|
||||
private BigDecimal creditLimit;
|
||||
|
||||
@ApiModelProperty("管理员账户id")
|
||||
private Long adminAccId;
|
||||
|
||||
}
|
||||
37
src/main/java/com/ai/da/model/vo/SubscriptionPlanVO.java
Normal file
37
src/main/java/com/ai/da/model/vo/SubscriptionPlanVO.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.ai.da.model.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class SubscriptionPlanVO {
|
||||
|
||||
@ApiModelProperty("id")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("组织id")
|
||||
private Long organizationId;
|
||||
|
||||
@ApiModelProperty("当前订阅开始时间")
|
||||
private Long currentPeriodStart;
|
||||
|
||||
@ApiModelProperty("当前订阅结束时间")
|
||||
private Long currentPeriodEnd;
|
||||
|
||||
@ApiModelProperty("当前订阅总的子账号数量")
|
||||
private Integer accountNum;
|
||||
|
||||
@ApiModelProperty("当前订阅可用积分上限")
|
||||
private BigDecimal creditLimit;
|
||||
|
||||
@ApiModelProperty("管理员账户id")
|
||||
private Long adminAccId;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -2237,7 +2237,7 @@ public class PythonService {
|
||||
private List<Integer> resolve(List<BigDecimal> list) {
|
||||
List<Integer> integerList = Lists.newArrayList();
|
||||
list.forEach(l -> {
|
||||
integerList.add(new Integer(l.intValue()));
|
||||
integerList.add(l.intValue());
|
||||
});
|
||||
return integerList;
|
||||
}
|
||||
|
||||
26
src/main/java/com/ai/da/service/SubscriptionPlanService.java
Normal file
26
src/main/java/com/ai/da/service/SubscriptionPlanService.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.ai.da.service;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.SubscriptionPlan;
|
||||
import com.ai.da.model.dto.SubscriptionPlanDTO;
|
||||
import com.ai.da.model.dto.SubscriptionPlanPageQuery;
|
||||
import com.ai.da.model.dto.UpdateSubscriptionPlanDTO;
|
||||
import com.ai.da.model.vo.SubscriptionPlanVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
public interface SubscriptionPlanService extends IService<SubscriptionPlan> {
|
||||
|
||||
void createPlan(SubscriptionPlanDTO subscriptionPlanDTO);
|
||||
|
||||
void updatePlan(UpdateSubscriptionPlanDTO updateDTO);
|
||||
|
||||
IPage<SubscriptionPlanVO> searchByPage(SubscriptionPlanPageQuery subscriptionPlanPageQuery);
|
||||
|
||||
void deletePlan(Long id);
|
||||
|
||||
void switchSubscriptionPlan(Long subscriptionPlanId, Long adminAccId);
|
||||
|
||||
void activeSubscriptionPlan();
|
||||
|
||||
void expireSubscription();
|
||||
}
|
||||
@@ -135,6 +135,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
@Resource
|
||||
private UserFollowService userFollowService;
|
||||
|
||||
@Resource
|
||||
private SubscriptionPlanMapper subscriptionPlanMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AccountPreLoginVO preLogin(AccountPreLoginDTO accountDTO) {
|
||||
@@ -2440,7 +2443,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean createSubAccount(AddSubAccountDTO addSubAccountDTO, Account adminAcc, int subUserRole) {
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(Account::getOrganizationName, adminAcc.getOrganizationName());
|
||||
qw.lambda().eq(Account::getOrganizationId, adminAcc.getOrganizationId());
|
||||
List<Account> accounts = accountMapper.selectList(qw);
|
||||
|
||||
// 校验子账号总数是否达上限
|
||||
@@ -2460,12 +2463,12 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
}
|
||||
|
||||
// 校验邮箱是否已加入组织
|
||||
if (isUserEmailExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserEmail())) {
|
||||
if (isUserEmailExists(adminAcc.getOrganizationId(), addSubAccountDTO.getUserEmail())) {
|
||||
throw new BusinessException("This organization already has an account with the same email.", ResultEnum.PROMPT.getCode());
|
||||
}
|
||||
|
||||
// 校验用户名是否同名
|
||||
if (isUsernameExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserName())) {
|
||||
if (isUsernameExists(adminAcc.getOrganizationId(), addSubAccountDTO.getUserName())) {
|
||||
throw new BusinessException("This organization already has an account with the same username.");
|
||||
}
|
||||
|
||||
@@ -2477,7 +2480,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
BigDecimal remainingCredits = adminRemainingCredits(adminAcc);
|
||||
// 将个人账号加入组织
|
||||
if (Objects.nonNull(subAccount) && personAccRole.contains(subAccount.getSystemUser())) {
|
||||
log.info("将用户{} 加入组织{}", addSubAccountDTO.getUserEmail(), adminAcc.getOrganizationName());
|
||||
log.info("将用户{} 加入组织{}", addSubAccountDTO.getUserEmail(), adminAcc.getOrganizationId());
|
||||
subAccount.setUserName(addSubAccountDTO.getUserName());
|
||||
if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserPassword())){
|
||||
subAccount.setUserPassword(addSubAccountDTO.getUserPassword());
|
||||
@@ -2489,8 +2492,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
}
|
||||
|
||||
subAccount.setSystemUser(subUserRole);
|
||||
subAccount.setOrganizationName(adminAcc.getOrganizationName());
|
||||
subAccount.setOrganizationId(adminAcc.getOrganizationId());
|
||||
subAccount.setParentId(adminAcc.getId());
|
||||
subAccount.setSubscriptionPlanId(adminAcc.getSubscriptionPlanId());
|
||||
if (Objects.nonNull(addSubAccountDTO.getCreditsUsageLimit())) {
|
||||
if (remainingCredits.compareTo(addSubAccountDTO.getCreditsUsageLimit()) < 0) {
|
||||
throw new BusinessException("Insufficient credits (Balance: " + remainingCredits + ").", ResultEnum.PROMPT.getCode());
|
||||
@@ -2553,10 +2557,13 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
subAccount.setIsTrial(0);
|
||||
subAccount.setIsBeginner(1);
|
||||
subAccount.setParentId(adminAcc.getId());
|
||||
subAccount.setOrganizationName(adminAcc.getOrganizationName());
|
||||
// subAccount.setOrganizationName(adminAcc.getOrganizationName());
|
||||
subAccount.setOrganizationId(adminAcc.getOrganizationId());
|
||||
subAccount.setSubscriptionPlanId(adminAcc.getSubscriptionPlanId());
|
||||
accountMapper.insert(subAccount);
|
||||
}
|
||||
updateById(adminAcc);
|
||||
syncAdminAccToSubscriptionPlan(adminAcc);
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@@ -2569,7 +2576,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
// 校验用户名是否同名
|
||||
if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserName())
|
||||
&& !exAccountInfo.getUserName().equals(addSubAccountDTO.getUserName())
|
||||
&& isUsernameExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserName())) {
|
||||
&& isUsernameExists(adminAcc.getOrganizationId(), addSubAccountDTO.getUserName())) {
|
||||
throw new BusinessException("This organization already has an account with the same username.");
|
||||
} else if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserName())
|
||||
&& !exAccountInfo.getUserName().equals(addSubAccountDTO.getUserName())) {
|
||||
@@ -2632,19 +2639,20 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
} else {
|
||||
baseMapper.updateById(exAccountInfo);
|
||||
baseMapper.updateById(adminAcc);
|
||||
syncAdminAccToSubscriptionPlan(adminAcc);
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
private boolean isUserEmailExists(String organizationName, String email) {
|
||||
private boolean isUserEmailExists(Long organizationId, String email) {
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(Account::getOrganizationName, organizationName).eq(Account::getUserEmail, email);
|
||||
qw.lambda().eq(Account::getOrganizationId, organizationId).eq(Account::getUserEmail, email);
|
||||
return accountMapper.selectCount(qw) > 0;
|
||||
}
|
||||
|
||||
private boolean isUsernameExists(String organizationName, String userName) {
|
||||
private boolean isUsernameExists(Long organizationId, String userName) {
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(Account::getOrganizationName, organizationName).eq(Account::getUserName, userName);
|
||||
qw.lambda().eq(Account::getOrganizationId, organizationId).eq(Account::getUserName, userName);
|
||||
return accountMapper.selectCount(qw) > 0;
|
||||
}
|
||||
|
||||
@@ -2738,6 +2746,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
.set(Account::getCredits, finalCredits)
|
||||
.set(Account::getCreditsUsage, null)
|
||||
.set(Account::getCreditsUsageLimit, null)
|
||||
.set(Account::getSubscriptionPlanId, null)
|
||||
.set(Account::getUpdateDate, new Date());
|
||||
baseMapper.update(null, updateWrapper);
|
||||
|
||||
@@ -2756,6 +2765,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
adminAcc.setCredits(adminAcc.getCredits().add(unusedCreditsTotal));
|
||||
adminAcc.setUpdateDate(new Date());
|
||||
baseMapper.updateById(adminAcc);
|
||||
syncAdminAccToSubscriptionPlan(adminAcc);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2776,13 +2786,27 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 用于管理员分配积分后,账号信息更新的同时,更新关联订阅计划的信息
|
||||
private void syncAdminAccToSubscriptionPlan(Account adminAcc) {
|
||||
if (Objects.isNull(adminAcc.getSubscriptionPlanId())) {
|
||||
return ;
|
||||
}
|
||||
SubscriptionPlan subscriptionPlan = subscriptionPlanMapper.selectById(adminAcc.getSubscriptionPlanId());
|
||||
if (Objects.nonNull(subscriptionPlan)) {
|
||||
subscriptionPlan.setCreditUsage(adminAcc.getCreditsUsage());
|
||||
subscriptionPlan.setUpdateTime(LocalDateTime.now());
|
||||
subscriptionPlanMapper.updateById(subscriptionPlan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PageBaseResponse<Account> subAccountList(SubAccountPageDTO subAccountPageDTO) {
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
Account account = accountMapper.selectById(authPrincipalVo);
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().ne(Account::getId, account.getId());
|
||||
qw.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
|
||||
qw.lambda().eq(Account::getOrganizationId, account.getOrganizationId());
|
||||
if (StringUtils.isNotBlank(subAccountPageDTO.getStartTime())) {
|
||||
qw.lambda().ge(Account::getCreateDate, subAccountPageDTO.getStartTime());
|
||||
}
|
||||
@@ -3443,7 +3467,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
int subUserRole = getSubUserRole(adminAcc.getSystemUser());
|
||||
|
||||
List<Account> accounts = accountMapper.selectList(new QueryWrapper<Account>()
|
||||
.eq("organization_name", adminAcc.getOrganizationName())
|
||||
.eq("organization_id", adminAcc.getOrganizationId())
|
||||
.eq("system_user", subUserRole)
|
||||
.select("user_name", "user_email", "user_password", "credits_usage_limit"));
|
||||
|
||||
@@ -3516,7 +3540,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
// 只有当前子账号数量为0时允许批量上传
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(Account::getSystemUser, 8)
|
||||
.eq(Account::getOrganizationName, parent.getOrganizationName());
|
||||
.eq(Account::getOrganizationId, parent.getOrganizationId());
|
||||
List<Account> accounts = accountMapper.selectList(qw);
|
||||
if (!accounts.isEmpty()) {
|
||||
throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode());
|
||||
@@ -3569,7 +3593,7 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
|
||||
@Override
|
||||
public Set<String> organizationNameSearch(String type, String name) {
|
||||
QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
/*QueryWrapper<Account> qw = new QueryWrapper<>();
|
||||
qw.lambda().ne(Account::getOrganizationName, "").isNotNull(Account::getOrganizationName);
|
||||
if (!StringUtil.isNullOrEmpty(name)) {
|
||||
qw.lambda().like(Account::getOrganizationName, name);
|
||||
@@ -3591,6 +3615,20 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
if (CollectionUtil.isNotEmpty(accountList)) {
|
||||
return accountList.stream().map(Account::getOrganizationName).collect(Collectors.toSet());
|
||||
}
|
||||
return new HashSet<>();*/
|
||||
|
||||
QueryWrapper<Organization> 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<Organization> organizations = organizationMapper.selectList(queryWrapper);
|
||||
if (CollectionUtil.isNotEmpty(organizations)) {
|
||||
return organizations.stream().map(Organization::getName).collect(Collectors.toSet());
|
||||
}
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
|
||||
@@ -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")){
|
||||
|
||||
@@ -0,0 +1,742 @@
|
||||
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;
|
||||
import com.ai.da.mapper.primary.OrganizationMapper;
|
||||
import com.ai.da.mapper.primary.SubscriptionPlanMapper;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.mapper.primary.entity.Organization;
|
||||
import com.ai.da.mapper.primary.entity.SubscriptionPlan;
|
||||
import com.ai.da.model.dto.SubscriptionPlanDTO;
|
||||
import com.ai.da.model.dto.SubscriptionPlanPageQuery;
|
||||
import com.ai.da.model.dto.UpdateSubscriptionPlanDTO;
|
||||
import com.ai.da.model.vo.SubscriptionPlanVO;
|
||||
import com.ai.da.service.SubscriptionPlanService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
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.common.base.Function;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SubscriptionPlanServiceImpl extends ServiceImpl<SubscriptionPlanMapper, SubscriptionPlan> implements SubscriptionPlanService {
|
||||
|
||||
private final AccountMapper accountMapper;
|
||||
|
||||
private final OrganizationMapper organizationMapper;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
// 创建
|
||||
@Override
|
||||
public void createPlan(SubscriptionPlanDTO subscriptionPlanDTO) {
|
||||
SubscriptionPlan subscriptionPlan = CopyUtil.copyObject(subscriptionPlanDTO, SubscriptionPlan.class);
|
||||
subscriptionPlan.setStatus(SubscriptionPlan.SubscriptionStatus.PENDING.name());
|
||||
subscriptionPlan.setName("DEFAULT_NAME");
|
||||
subscriptionPlan.setCreditUsage(BigDecimal.ZERO);
|
||||
subscriptionPlan.setCreateTime(LocalDateTime.now());
|
||||
if (Objects.isNull(subscriptionPlanDTO.getCreditLimit())) {
|
||||
subscriptionPlan.setCreditLimit(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
baseMapper.insert(subscriptionPlan);
|
||||
}
|
||||
|
||||
// 更新 到期时间、积分总量、已使用积分量
|
||||
@Override
|
||||
public void updatePlan(UpdateSubscriptionPlanDTO updateDTO) {
|
||||
if (Objects.isNull(updateDTO.getId())) {
|
||||
throw new BusinessException("id.cannot.be.empty");
|
||||
}
|
||||
|
||||
SubscriptionPlan subscriptionPlan = baseMapper.selectById(updateDTO.getId());
|
||||
if (Objects.isNull(subscriptionPlan)) {
|
||||
throw new BusinessException("unknown.subscription.plan");
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCurrentPeriodStart()) && !updateDTO.getCurrentPeriodStart().equals(subscriptionPlan.getCurrentPeriodStart())) {
|
||||
subscriptionPlan.setCurrentPeriodStart(updateDTO.getCurrentPeriodStart());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCurrentPeriodEnd()) && !updateDTO.getCurrentPeriodEnd().equals(subscriptionPlan.getCurrentPeriodEnd())) {
|
||||
subscriptionPlan.setCurrentPeriodEnd(updateDTO.getCurrentPeriodEnd());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getAccountNum()) && !updateDTO.getAccountNum().equals(subscriptionPlan.getAccountNum())) {
|
||||
subscriptionPlan.setAccountNum(updateDTO.getAccountNum());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getCreditLimit()) && !updateDTO.getCreditLimit().equals(subscriptionPlan.getCreditLimit())) {
|
||||
subscriptionPlan.setCreditLimit(updateDTO.getCreditLimit());
|
||||
}
|
||||
|
||||
if (Objects.nonNull(updateDTO.getAdminAccId()) && !updateDTO.getAdminAccId().equals(subscriptionPlan.getAdminAccId())) {
|
||||
subscriptionPlan.setAdminAccId(updateDTO.getAdminAccId());
|
||||
}
|
||||
|
||||
subscriptionPlan.setUpdateTime(LocalDateTime.now());
|
||||
updateById(subscriptionPlan);
|
||||
|
||||
}
|
||||
|
||||
public void updatePlan() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 查找 根据入参提供的参数进行分页查询
|
||||
@Override
|
||||
public IPage<SubscriptionPlanVO> searchByPage(SubscriptionPlanPageQuery subscriptionPlanPageQuery) {
|
||||
// 1. 参数校验
|
||||
validatePageQuery(subscriptionPlanPageQuery);
|
||||
|
||||
// 2. 构建查询条件
|
||||
LambdaQueryWrapper<SubscriptionPlan> queryWrapper = buildQueryWrapper(subscriptionPlanPageQuery);
|
||||
|
||||
// 3. 执行分页查询
|
||||
Page<SubscriptionPlan> page = new Page<>(subscriptionPlanPageQuery.getPage(), subscriptionPlanPageQuery.getSize());
|
||||
IPage<SubscriptionPlan> resultPage = baseMapper.selectPage(page, queryWrapper);
|
||||
|
||||
// 4. 转换为VO并返回
|
||||
return resultPage.convert((Function<SubscriptionPlan, SubscriptionPlanVO>) plan -> CopyUtil.copyObject(plan, SubscriptionPlanVO.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验
|
||||
*/
|
||||
private void validatePageQuery(SubscriptionPlanPageQuery query) {
|
||||
// 基本分页参数校验(JSR-303已校验,这里做额外业务校验)
|
||||
if (query.getPage() <= 0) {
|
||||
throw new BusinessException("page.num.limit");
|
||||
}
|
||||
if (query.getSize() <= 0 || query.getSize() > 100) {
|
||||
throw new BusinessException("page.size.limit");
|
||||
}
|
||||
|
||||
// 时间格式校验
|
||||
if (StringUtils.isNotBlank(query.getStartTime()) && StringUtils.isNotBlank(query.getEndTime())) {
|
||||
try {
|
||||
LocalDateTime start = parseDateTime(query.getStartTime());
|
||||
LocalDateTime end = parseDateTime(query.getEndTime());
|
||||
assert start != null;
|
||||
if (start.isAfter(end)) {
|
||||
throw new BusinessException("the.start.time.cannot.be.later.than.the.end.time");
|
||||
}
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new BusinessException("invalid.time.format");
|
||||
}
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(query.getStatus())) {
|
||||
for (String status : query.getStatus()) {
|
||||
try {
|
||||
SubscriptionPlan.SubscriptionStatus.valueOf(status.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("未知订阅状态:{}", status);
|
||||
throw new BusinessException("unknown.subscription.status");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建查询条件
|
||||
*/
|
||||
private LambdaQueryWrapper<SubscriptionPlan> buildQueryWrapper(SubscriptionPlanPageQuery query) {
|
||||
LambdaQueryWrapper<SubscriptionPlan> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 精确匹配条件
|
||||
if (query.getId() != null) {
|
||||
wrapper.eq(SubscriptionPlan::getId, query.getId());
|
||||
}
|
||||
if (query.getOrganizationId() != null) {
|
||||
wrapper.eq(SubscriptionPlan::getOrganizationId, query.getOrganizationId());
|
||||
}
|
||||
if (query.getAdminAccId() != null) {
|
||||
wrapper.eq(SubscriptionPlan::getAdminAccId, query.getAdminAccId());
|
||||
}
|
||||
|
||||
// 时间范围查询
|
||||
if (StringUtils.isNotBlank(query.getStartTime())) {
|
||||
LocalDateTime startTime = parseDateTime(query.getStartTime());
|
||||
wrapper.ge(SubscriptionPlan::getCreateTime, startTime);
|
||||
}
|
||||
if (StringUtils.isNotBlank(query.getEndTime())) {
|
||||
LocalDateTime endTime = parseDateTime(query.getEndTime());
|
||||
wrapper.le(SubscriptionPlan::getCreateTime, endTime);
|
||||
}
|
||||
|
||||
// 状态匹配
|
||||
if (!CollectionUtils.isEmpty(query.getStatus())) {
|
||||
wrapper.in(SubscriptionPlan::getStatus, query.getStatus());
|
||||
}
|
||||
|
||||
// 默认按创建时间倒序排序
|
||||
wrapper.eq(SubscriptionPlan::getIsDeleted, 0)
|
||||
.orderByDesc(SubscriptionPlan::getCreateTime);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间字符串解析(支持多种格式)
|
||||
*/
|
||||
private LocalDateTime parseDateTime(String timeStr) {
|
||||
if (StringUtils.isBlank(timeStr)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试解析完整时间格式
|
||||
try {
|
||||
return LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
} catch (DateTimeParseException e1) {
|
||||
// 尝试解析日期格式(自动补全时间为00:00:00)
|
||||
try {
|
||||
LocalDate date = LocalDate.parse(timeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
return date.atStartOfDay();
|
||||
} catch (DateTimeParseException e2) {
|
||||
throw new DateTimeParseException("时间格式错误,请使用yyyy-MM-dd或yyyy-MM-dd HH:mm:ss格式", timeStr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除(逻辑删除)
|
||||
@Override
|
||||
public void deletePlan(Long id) {
|
||||
Long adminAccountId = UserContext.getUserHolder().getId();
|
||||
// 1. 参数基础校验
|
||||
if (id == null || id <= 0) {
|
||||
throw new BusinessException("ID.cannot.be.empty.and.must.be.greater.than.0");
|
||||
}
|
||||
|
||||
// 2. 检查数据是否存在
|
||||
SubscriptionPlan plan = baseMapper.selectById(id);
|
||||
if (plan == null) {
|
||||
throw new BusinessException("subscription.plan.does.not.exist");
|
||||
}
|
||||
|
||||
// 3. 检查是否已被删除(防止重复删除) 一般情况下走不到,逻辑删除的数据通过mybatis-plus查不到
|
||||
if (plan.getIsDeleted() == 1) {
|
||||
throw new BusinessException("subscription.plan.has.been.deleted");
|
||||
}
|
||||
|
||||
// 4. 检查业务约束条件(例如:是否有正在使用的订单等)
|
||||
checkBusinessConstraints(plan);
|
||||
|
||||
// 5. 执行逻辑删除
|
||||
LambdaUpdateWrapper<SubscriptionPlan> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SubscriptionPlan::getId, id)
|
||||
.set(SubscriptionPlan::getIsDeleted, 1)
|
||||
.set(SubscriptionPlan::getDeleteBy, adminAccountId);
|
||||
|
||||
int rows = baseMapper.update(null, wrapper);
|
||||
if (rows == 0) {
|
||||
throw new BusinessException("deletion.failed.please.try.again.later");
|
||||
}
|
||||
|
||||
// 6. 记录操作日志
|
||||
log.info("管理员{}, 删除订阅计划 {}", adminAccountId, plan);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查业务约束条件
|
||||
*/
|
||||
private void checkBusinessConstraints(SubscriptionPlan plan) {
|
||||
// 检查是否有活跃的用户关联
|
||||
Long activeSubAcc = countActiveSubAccByPlanId(plan.getId());
|
||||
if (activeSubAcc > 0) {
|
||||
throw new BusinessException("users.currently.using.this.plan");
|
||||
}
|
||||
|
||||
// 检查是否在有效期内
|
||||
if (plan.getCurrentPeriodEnd() != null && isExpired(plan.getCurrentPeriodEnd())) {
|
||||
throw new BusinessException("valid.subscription.period");
|
||||
}
|
||||
}
|
||||
|
||||
private Long countActiveSubAccByPlanId(Long planId) {
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Account::getSubscriptionPlanId, planId);
|
||||
return accountMapper.selectCount(queryWrapper);
|
||||
}
|
||||
|
||||
// 判断是否已过期
|
||||
private boolean isExpired(Long currentPeriodEnd) {
|
||||
if (currentPeriodEnd == null) {
|
||||
return false; // 永久有效
|
||||
}
|
||||
long currentTimestamp = System.currentTimeMillis() / 1000;
|
||||
return currentPeriodEnd < currentTimestamp;
|
||||
}
|
||||
|
||||
// todo 切换管理员的账号
|
||||
|
||||
// 定时器更新管理员状态
|
||||
/* public void updateEduAdminAccount() {
|
||||
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart
|
||||
|
||||
// 2. 当检测到有今天内开始有效的订阅,更新绑定的管理员的账号
|
||||
|
||||
// 3. 更新管理的信息包括,①根据订阅结束时间延长管理员账号有效期 ②根据积分上限更新管理员的积分,如果当前管理员正处于一个订阅中,则不做更新,但是允许切换
|
||||
}*/
|
||||
|
||||
public void activeSubscriptionPlan() {
|
||||
log.info("开始执行订阅计划生效检查...");
|
||||
|
||||
// 1. 扫描所有的订阅计划的开始时间currentPeriodStart,找出今天开始生效的计划
|
||||
List<SubscriptionPlan> todayActivePlans = findTodayActivePlans();
|
||||
|
||||
if (CollectionUtils.isEmpty(todayActivePlans)) {
|
||||
log.info("今日没有需要生效的订阅计划");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("发现{}个今日生效的订阅计划", todayActivePlans.size());
|
||||
|
||||
// 2. 处理每个今天开始生效的订阅计划
|
||||
for (SubscriptionPlan plan : todayActivePlans) {
|
||||
try {
|
||||
processActiveSubscriptionPlan(plan);
|
||||
// 3. 修改订阅状态
|
||||
plan.setStatus(SubscriptionPlan.SubscriptionStatus.ACTIVE.name());
|
||||
plan.setUpdateTime(LocalDateTime.now());
|
||||
baseMapper.updateById(plan);
|
||||
} catch (Exception e) {
|
||||
log.error("处理订阅计划失败,ID: {}, 错误: {}", plan.getId(), e.getMessage(), e);
|
||||
// 继续处理其他计划,不中断整体流程
|
||||
}
|
||||
}
|
||||
|
||||
log.info("订阅计划生效检查执行完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找今天开始生效的订阅计划
|
||||
*/
|
||||
private List<SubscriptionPlan> findTodayActivePlans() {
|
||||
// 获取今天的开始和结束时间戳(秒级)
|
||||
long todayStart = getTodayStartTimestamp();
|
||||
long todayEnd = getTodayEndTimestamp();
|
||||
|
||||
log.debug("扫描时间范围: {} - {} (今日)", formatTimestamp(todayStart), formatTimestamp(todayEnd));
|
||||
|
||||
// 查询今天开始生效的订阅计划
|
||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("is_deleted", 0) // 未删除
|
||||
.between("current_period_start", todayStart, todayEnd) // 今天开始生效
|
||||
.orderByAsc("current_period_start"); // 按开始时间排序
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个生效的订阅计划
|
||||
*/
|
||||
private void processActiveSubscriptionPlan(SubscriptionPlan plan) {
|
||||
log.info("处理生效订阅计划,ID: {}, 组织ID: {}, 管理员ID: {}",
|
||||
plan.getId(), plan.getOrganizationId(), plan.getAdminAccId());
|
||||
|
||||
// 获取关联的管理员账号
|
||||
Account adminAccount = accountMapper.selectById(plan.getAdminAccId());
|
||||
if (adminAccount == null) {
|
||||
log.error("管理员账号不存在,ID: {}", plan.getAdminAccId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 检查管理员当前是否处于其他有效订阅中
|
||||
if (isAccountInActiveSubscription(adminAccount, plan.getId())) {
|
||||
log.info("管理员ID: {} 已处于有效订阅中,本次不修改账户信息", adminAccount.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 更新管理员账号信息
|
||||
updateAdminAccount(adminAccount, plan);
|
||||
|
||||
log.info("订阅计划处理完成,ID: {}", plan.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新管理员账号信息
|
||||
*/
|
||||
private void updateAdminAccount(Account account, SubscriptionPlan plan) {
|
||||
Account updateAccount = new Account();
|
||||
updateAccount.setId(account.getId());
|
||||
|
||||
// ① 根据订阅结束时间延长管理员账号有效期validEndTime
|
||||
// Long newValidEndTime = calculateNewValidEndTime(account, plan);
|
||||
updateAccount.setValidEndTime(plan.getCurrentPeriodEnd() * 1000);
|
||||
|
||||
// ② 根据积分上限更新管理员的积分credits和身份systemUser
|
||||
// 暂时保留管理员自己购买的积分
|
||||
if ((account.getSystemUser() == 5 || account.getSystemUser() == 7)
|
||||
&& Objects.nonNull(account.getCreditsUsageLimit())) {
|
||||
BigDecimal leftCredits = account.getCredits().add(account.getCreditsUsage()).subtract(account.getCreditsUsageLimit());
|
||||
if (leftCredits.compareTo(BigDecimal.ZERO) > 0) {
|
||||
updateAccount.setCredits(plan.getCreditLimit().add(leftCredits));
|
||||
} else {
|
||||
updateAccount.setCredits(plan.getCreditLimit());
|
||||
}
|
||||
} else {
|
||||
updateAccount.setCredits(plan.getCreditLimit().add(account.getCredits()));
|
||||
}
|
||||
|
||||
// 根据组织ID判断用户类型
|
||||
Integer newSystemUser = determineSystemUserType(plan);
|
||||
updateAccount.setSystemUser(newSystemUser);
|
||||
|
||||
// 更新子账号数量限制
|
||||
updateAccount.setCreditsUsage(plan.getCreditUsage());
|
||||
updateAccount.setCreditsUsageLimit(plan.getCreditLimit());
|
||||
updateAccount.setSubAccountNum(plan.getAccountNum());
|
||||
|
||||
// 关联订阅计划ID
|
||||
updateAccount.setSubscriptionPlanId(plan.getId());
|
||||
|
||||
// 更新组织信息
|
||||
updateAccount.setOrganizationId(plan.getOrganizationId());
|
||||
|
||||
// 如果是组织管理员,设置isAdmin标志
|
||||
/* if (isOrganizationAdmin(newSystemUser)) {
|
||||
updateAccount.setIsAdmin(1);
|
||||
}*/
|
||||
|
||||
// 执行更新
|
||||
int rows = accountMapper.updateById(updateAccount);
|
||||
if (rows > 0) {
|
||||
log.info("管理员账号更新成功,ID: {}, 有效期至: {}, 用户类型: {}, 积分: {}",
|
||||
account.getId(),
|
||||
formatTimestamp(plan.getCurrentPeriodEnd()),
|
||||
newSystemUser,
|
||||
plan.getCreditLimit());
|
||||
|
||||
} else {
|
||||
log.error("管理员账号更新失败,ID: {}", account.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算新的有效期结束时间
|
||||
*/
|
||||
private Long calculateNewValidEndTime(Account account, SubscriptionPlan plan) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
Long currentValidEndTime = account.getValidEndTime();
|
||||
|
||||
// 如果当前有效期晚于现在,则延长有效期
|
||||
if (currentValidEndTime != null && currentValidEndTime > now) {
|
||||
// 计算订阅的时长(秒)
|
||||
long subscriptionDuration = plan.getCurrentPeriodEnd() - plan.getCurrentPeriodStart();
|
||||
|
||||
// 延长现有有效期
|
||||
return currentValidEndTime + subscriptionDuration * 1000;
|
||||
} else {
|
||||
// 否则使用订阅结束时间
|
||||
return plan.getCurrentPeriodEnd() * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据计划确定用户类型
|
||||
*/
|
||||
private Integer determineSystemUserType(SubscriptionPlan plan) {
|
||||
// 根据组织ID, 去organization表中查询判断是学校还是企业
|
||||
Long orgId = plan.getOrganizationId();
|
||||
|
||||
Organization organization = organizationMapper.selectById(orgId);
|
||||
|
||||
if (Objects.isNull(organization)) {
|
||||
log.error("未知组织id: {}", orgId);
|
||||
throw new BusinessException("unknown.organization");
|
||||
}
|
||||
if (organization.getType().equals("Enterprise")) {
|
||||
return 5; // 学校管理员
|
||||
} else if (organization.getType().equals("Education")) {
|
||||
return 7; // 企业管理员
|
||||
} else {
|
||||
log.error("组织id未知组织类型");
|
||||
}
|
||||
|
||||
// 默认为教育管理员
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天开始的秒级时间戳
|
||||
*/
|
||||
private long getTodayStartTimestamp() {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDateTime startOfDay = today.atStartOfDay();
|
||||
return startOfDay.atZone(ZoneId.systemDefault()).toEpochSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天结束的秒级时间戳
|
||||
*/
|
||||
private long getTodayEndTimestamp() {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDateTime endOfDay = today.atTime(23, 59, 59);
|
||||
return endOfDay.atZone(ZoneId.systemDefault()).toEpochSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间戳为可读字符串
|
||||
*/
|
||||
private String formatTimestamp(Long timestamp) {
|
||||
if (timestamp == null) return "null";
|
||||
Instant instant = Instant.ofEpochSecond(timestamp);
|
||||
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断管理员在当前时间是否有活跃订阅(用于新订阅激活判断)
|
||||
* 规则:只要有一个活跃订阅,就不更新管理员信息
|
||||
*/
|
||||
private boolean isAccountInActiveSubscription(Account account, Long excludePlanId) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
|
||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("is_deleted", 0)
|
||||
.eq("admin_acc_id", account.getId())
|
||||
.ne(excludePlanId != null, "id", excludePlanId)
|
||||
.le("current_period_start", now) // 已开始
|
||||
.ge("current_period_end", now); // 未结束
|
||||
|
||||
Long count = baseMapper.selectCount(queryWrapper);
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
// 定时器清除到期订阅,查看管理员是否还有其他处于订阅中的计划并更新管理员信息
|
||||
public void expireSubscription() {
|
||||
// 1. 查询有哪些已过期订阅
|
||||
List<SubscriptionPlan> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找最近过期的订阅(过去24小时内到期的)
|
||||
*/
|
||||
private List<SubscriptionPlan> findRecentlyExpiredPlans() {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
long yesterday = now - (24 * 60 * 60); // 24小时前
|
||||
|
||||
log.debug("扫描时间范围: {} - {} (过去24h)", formatTimestamp(yesterday), formatTimestamp(now));
|
||||
|
||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("is_deleted", 0)
|
||||
.between("current_period_end", yesterday, now) // 过去24小时内到期
|
||||
.orderByAsc("current_period_end");
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整的订阅到期处理流程
|
||||
*/
|
||||
private void processExpiringSubscriptionPlan(SubscriptionPlan plan) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
|
||||
// 1. 检查订阅是否真的过期
|
||||
if (now <= plan.getCurrentPeriodEnd()) {
|
||||
log.info("订阅{}尚未完全过期,跳过处理", plan.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取关联的管理员账号
|
||||
Account adminAccount = accountMapper.selectById(plan.getAdminAccId());
|
||||
if (adminAccount == null) {
|
||||
log.error("关联的管理员账号不存在,ID: {}", plan.getAdminAccId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 检查管理员当前激活的订阅是否是这一个
|
||||
SubscriptionPlan currentActivatedPlan = getCurrentActivatedSubscription(adminAccount);
|
||||
boolean isCurrentlyActivated = currentActivatedPlan != null &&
|
||||
currentActivatedPlan.getId().equals(plan.getId());
|
||||
|
||||
if (isCurrentlyActivated) {
|
||||
// 4. 管理员当前正使用这个订阅,需要处理切换或降级
|
||||
handleSubscriptionExpiration(adminAccount, plan);
|
||||
} else {
|
||||
// 5. 管理员当前未使用这个订阅,只处理订阅关系状态
|
||||
log.info("订阅{}已过期,但管理员{}未激活此订阅", plan.getId(), adminAccount.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取管理员当前激活的订阅计划(account.subscriptionPlanId对应的)
|
||||
*/
|
||||
private SubscriptionPlan getCurrentActivatedSubscription(Account account) {
|
||||
if (account.getSubscriptionPlanId() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SubscriptionPlan plan = baseMapper.selectById(account.getSubscriptionPlanId());
|
||||
if (plan == null || plan.getIsDeleted() == 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅到期处理逻辑(使用 getAccountActiveSubscriptions)
|
||||
*/
|
||||
private void handleSubscriptionExpiration(Account adminAccount, SubscriptionPlan expiringPlan) {
|
||||
log.info("开始处理订阅{}到期,管理员ID: {}", expiringPlan.getId(), adminAccount.getId());
|
||||
|
||||
// 1. 获取管理员的其他活跃订阅(排除当前到期订阅)
|
||||
List<SubscriptionPlan> otherActiveSubscriptions =
|
||||
getAccountActiveSubscriptions(adminAccount, expiringPlan.getId());
|
||||
|
||||
if (!otherActiveSubscriptions.isEmpty()) {
|
||||
// 2. 有其他活跃订阅,找到开始最早的进行切换
|
||||
SubscriptionPlan earliestActivePlan = otherActiveSubscriptions.get(0); // 已按开始时间排序
|
||||
|
||||
log.info("管理员{}还有其他{}个活跃订阅,切换到开始最早的订阅{}",
|
||||
adminAccount.getId(), otherActiveSubscriptions.size(), earliestActivePlan.getId());
|
||||
|
||||
// 3. 切换到开始最早的订阅
|
||||
updateAdminAccount(adminAccount, earliestActivePlan);
|
||||
|
||||
// 4. 修改订阅状态
|
||||
earliestActivePlan.setStatus(SubscriptionPlan.SubscriptionStatus.ACTIVE.name());
|
||||
earliestActivePlan.setUpdateTime(LocalDateTime.now());
|
||||
baseMapper.updateById(earliestActivePlan);
|
||||
|
||||
} else {
|
||||
// 5. 没有其他活跃订阅,将管理员降级为游客
|
||||
log.info("管理员{}没有其他活跃订阅,降级为游客", adminAccount.getId());
|
||||
|
||||
// downgradeAccountToVisitor(adminAccount);
|
||||
// todo toVisitor 需要更新其他字段,如subscriptionPlanId, parentId
|
||||
accountMapper.toVisitor(adminAccount.getId());
|
||||
log.info("管理员账号{}已降级为游客", adminAccount.getId());
|
||||
|
||||
}
|
||||
|
||||
// 7. 处理该订阅下的子账号
|
||||
processChildAccountsForExpiredSubscription(expiringPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取管理员当前的活跃订阅(用于订阅到期判断)
|
||||
* 返回:按开始时间排序的活跃订阅列表
|
||||
*/
|
||||
private List<SubscriptionPlan> getAccountActiveSubscriptions(Account account, Long excludePlanId) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
|
||||
QueryWrapper<SubscriptionPlan> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(SubscriptionPlan::getIsDeleted, 0)
|
||||
.eq(SubscriptionPlan::getAdminAccId, account.getId())
|
||||
.ne(excludePlanId != null, SubscriptionPlan::getId, excludePlanId)
|
||||
.le(SubscriptionPlan::getCurrentPeriodStart, now) // 已开始
|
||||
.ge(SubscriptionPlan::getCurrentPeriodEnd, now) // 未结束
|
||||
.orderByAsc(SubscriptionPlan::getCurrentPeriodStart); // 按开始时间升序
|
||||
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
private void processChildAccountsForExpiredSubscription(SubscriptionPlan expiredPlan) {
|
||||
log.info("开始处理过期订阅下的子账号,订阅ID: {},组织ID: {}",
|
||||
expiredPlan.getId(), expiredPlan.getOrganizationId());
|
||||
|
||||
// 1. 查找该订阅下的所有子账号
|
||||
List<Account> childAccounts = findChildAccountsBySubscription(expiredPlan);
|
||||
if (CollectionUtils.isEmpty(childAccounts)) {
|
||||
log.info("订阅{}下没有需要处理的子账号", expiredPlan.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("找到{}个子账号需要处理,订阅ID: {}", childAccounts.size(), expiredPlan.getId());
|
||||
for (Account account : childAccounts) {
|
||||
accountMapper.toVisitor(account.getId());
|
||||
log.info("账号{}已降级为游客", account.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Account> findChildAccountsBySubscription(SubscriptionPlan expiredPlan) {
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().eq(Account::getOrganizationId, expiredPlan.getOrganizationId())
|
||||
.eq(Account::getSubscriptionPlanId, expiredPlan.getId());
|
||||
|
||||
return accountMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员切换当前管理的订阅
|
||||
*/
|
||||
public void switchSubscriptionPlan(Long subscriptionPlanId, Long adminAccId) {
|
||||
// 1. 权限校验
|
||||
Long accountId = UserContext.getUserHolder().getId();
|
||||
SubscriptionPlan subscriptionPlan = baseMapper.selectById(subscriptionPlanId);
|
||||
if (Objects.isNull(subscriptionPlan)) {
|
||||
throw new BusinessException("unknown.subscription.plan");
|
||||
}
|
||||
if (subscriptionPlan.getStatus().equals(SubscriptionPlan.SubscriptionStatus.EXPIRED.name())) {
|
||||
throw new BusinessException("subscription.has.expired");
|
||||
}
|
||||
if (!accountId.equals(subscriptionPlan.getAdminAccId()) && !accountId.equals(87L)) {
|
||||
throw new BusinessException("have.no.permission");
|
||||
}
|
||||
|
||||
// 2. 更新管理员积分
|
||||
if (!accountId.equals(87L)) {
|
||||
adminAccId = accountId;
|
||||
}
|
||||
Account account = accountMapper.selectById(adminAccId);
|
||||
if (Objects.isNull(account)) {
|
||||
throw new BusinessException("unknown.administrator.user");
|
||||
}
|
||||
if (!adminAccId.equals(subscriptionPlan.getAdminAccId())) {
|
||||
log.info("用户:{} 没有权限管理订阅:{}", adminAccId, subscriptionPlanId);
|
||||
throw new BusinessException("no.permission.manage.subscription", ResultEnum.PROMPT.getCode());
|
||||
}
|
||||
if (account.getSubscriptionPlanId().equals(subscriptionPlanId)) {
|
||||
log.info("用户:{} 当前正在管理订阅 {}", adminAccId, subscriptionPlanId);
|
||||
return;
|
||||
}
|
||||
|
||||
updateAdminAccount(account, subscriptionPlan);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,8 @@
|
||||
<update id="toVisitor">
|
||||
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}
|
||||
</update>
|
||||
|
||||
|
||||
@@ -189,6 +189,22 @@ do.not.have.the.permission.to.delete.this.comment=You do not have the permission
|
||||
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:
|
||||
|
||||
@@ -185,6 +185,22 @@ do.not.have.the.permission.to.delete.this.comment=您没有权限删除此评论
|
||||
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:
|
||||
|
||||
@@ -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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
|
||||
|
||||
Reference in New Issue
Block a user