feat(tenant-balance): 新增租户余额提现功能
新增 /withdraw 接口,支持租户在限定日期内发起余额提现;增加冻结金额字段;补充提现相关错误码与日期校验逻辑。
This commit is contained in:
@@ -126,6 +126,14 @@ public class TenantBalanceController {
|
||||
return success(BeanUtils.toBean(tenantBalancePage, TenantBalanceRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/withdraw")
|
||||
@Operation(summary = "租户提现")
|
||||
@PreAuthorize("@ss.hasPermission('system:tenant-balance:withdraw')")
|
||||
public CommonResult<Boolean> withdraw(@Valid @RequestBody TenantBalanceWithdrawReqVO withdrawReqVO) {
|
||||
tenantBalanceService.withdraw(withdrawReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
// ==================== 子表(租户积分记录) ====================
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.yolo.keyboard.controller.admin.tenantbalance.vo;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.yolo.keyboard.framework.common.pojo.PageParam;
|
||||
@@ -23,5 +25,6 @@ public class TenantBalancePageReqVO extends PageParam {
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
|
||||
|
||||
@Schema(description = "冻结金额")
|
||||
private BigDecimal frozenAmt;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.yolo.keyboard.controller.admin.tenantbalance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -28,5 +30,6 @@ public class TenantBalanceRespVO {
|
||||
@ExcelProperty("更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
|
||||
@Schema(description = "冻结金额")
|
||||
private BigDecimal frozenAmt;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.yolo.keyboard.controller.admin.tenantbalance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
@@ -26,5 +28,7 @@ public class TenantBalanceSaveReqVO {
|
||||
@NotNull(message = "更新时间不能为空")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Schema(description = "冻结金额")
|
||||
private BigDecimal frozenAmt;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yolo.keyboard.controller.admin.tenantbalance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 管理后台 - 租户余额提现 Request VO
|
||||
*/
|
||||
@Schema(description = "管理后台 - 租户余额提现 Request VO")
|
||||
@Data
|
||||
public class TenantBalanceWithdrawReqVO {
|
||||
|
||||
@Schema(description = "提现金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
@NotNull(message = "提现金额不能为空")
|
||||
@DecimalMin(value = "0.01", message = "提现金额必须大于0")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "备注", example = "提现备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -44,5 +44,9 @@ public class TenantBalanceDO extends BaseDO {
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 冻结金额
|
||||
*/
|
||||
private BigDecimal frozenAmt;
|
||||
|
||||
}
|
||||
@@ -129,5 +129,11 @@ public interface TenantBalanceService {
|
||||
*/
|
||||
TenantBalanceTransactionDO getTenantBalanceTransaction(Long id);
|
||||
|
||||
/**
|
||||
* 租户提现
|
||||
*
|
||||
* @param withdrawReqVO 提现信息
|
||||
*/
|
||||
void withdraw(@Valid TenantBalanceWithdrawReqVO withdrawReqVO);
|
||||
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.yolo.keyboard.service.tenantbalance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO;
|
||||
import com.yolo.keyboard.dal.mysql.tenantbalancetransaction.TenantBalanceTransactionMapper;
|
||||
import com.yolo.keyboard.framework.common.util.collection.CollectionUtils;
|
||||
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder;
|
||||
import com.yolo.keyboard.module.infra.api.config.ConfigApi;
|
||||
import com.yolo.keyboard.utils.BizNoGenerator;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -13,6 +15,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import com.yolo.keyboard.controller.admin.tenantbalance.vo.*;
|
||||
import com.yolo.keyboard.dal.dataobject.tenantbalance.TenantBalanceDO;
|
||||
@@ -27,8 +30,7 @@ import com.yolo.keyboard.module.system.dal.mysql.tenant.TenantMapper;
|
||||
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.diffList;
|
||||
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TENANT_BALANCE_NOT_EXISTS;
|
||||
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TENANT_BALANCE_TRANSACTION_NOT_EXISTS;
|
||||
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 租户余额 Service 实现类
|
||||
@@ -48,6 +50,11 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
||||
@Resource
|
||||
private TenantMapper tenantMapper;
|
||||
|
||||
@Resource
|
||||
private ConfigApi configApi;
|
||||
|
||||
private static final String WITHDRAW_DAYS_CONFIG_KEY = "WITHDRAW-DAYS";
|
||||
|
||||
@Override
|
||||
public Long createTenantBalance(TenantBalanceSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
@@ -211,5 +218,87 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
||||
tenantBalanceTransactionMapper.deleteByTenantIds(tenantIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void withdraw(TenantBalanceWithdrawReqVO withdrawReqVO) {
|
||||
// 1. 校验是否在提现日期范围内
|
||||
validateWithdrawDate();
|
||||
|
||||
// 2. 获取当前租户的余额记录
|
||||
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||
TenantBalanceDO balance = tenantBalanceMapper.selectById(tenantId);
|
||||
if (balance == null) {
|
||||
throw exception(TENANT_BALANCE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 3. 校验余额是否充足
|
||||
BigDecimal withdrawAmount = withdrawReqVO.getAmount();
|
||||
if (balance.getBalance().compareTo(withdrawAmount) < 0) {
|
||||
throw exception(TENANT_BALANCE_WITHDRAW_INSUFFICIENT);
|
||||
}
|
||||
|
||||
// 4. 扣减余额
|
||||
BigDecimal newBalance = balance.getBalance().subtract(withdrawAmount);
|
||||
balance.setBalance(newBalance);
|
||||
int updateCount = tenantBalanceMapper.updateById(balance);
|
||||
if (updateCount == 0) {
|
||||
throw exception(TENANT_BALANCE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 5. 创建提现交易记录
|
||||
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
||||
.bizNo(BizNoGenerator.generate("WITHDRAW"))
|
||||
.points(withdrawAmount.negate()) // 提现为负数
|
||||
.balance(newBalance)
|
||||
.tenantId(tenantId)
|
||||
.type("WITHDRAW")
|
||||
.description("余额提现")
|
||||
.remark(withdrawReqVO.getRemark())
|
||||
.operatorId(tenantId)
|
||||
.build();
|
||||
tenantBalanceTransactionMapper.insert(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否在提现日期范围内
|
||||
* 配置格式:{"start": 25, "days": 6}
|
||||
* 表示每月从第 start 天开始,连续 days 天可以提现
|
||||
*/
|
||||
private void validateWithdrawDate() {
|
||||
// 获取提现日期配置
|
||||
String configValue = configApi.getConfigValueByKey(WITHDRAW_DAYS_CONFIG_KEY);
|
||||
if (configValue == null || configValue.isEmpty()) {
|
||||
throw exception(TENANT_BALANCE_WITHDRAW_CONFIG_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 解析配置
|
||||
cn.hutool.json.JSONObject config = JSONUtil.parseObj(configValue);
|
||||
int startDay = config.getInt("start");
|
||||
int days = config.getInt("days");
|
||||
|
||||
// 获取当前日期
|
||||
LocalDate today = LocalDate.now();
|
||||
int currentDay = today.getDayOfMonth();
|
||||
int lastDayOfMonth = today.lengthOfMonth();
|
||||
|
||||
// 计算提现日期范围
|
||||
// 如果 start + days - 1 超过当月最后一天,需要处理跨月的情况
|
||||
int endDay = startDay + days - 1;
|
||||
|
||||
boolean isInRange;
|
||||
if (endDay <= lastDayOfMonth) {
|
||||
// 不跨月:当前日期在 [startDay, endDay] 范围内
|
||||
isInRange = currentDay >= startDay && currentDay <= endDay;
|
||||
} else {
|
||||
// 跨月:当前日期在 [startDay, 月末] 或 [1, endDay - lastDayOfMonth] 范围内
|
||||
int nextMonthEndDay = endDay - lastDayOfMonth;
|
||||
isInRange = currentDay >= startDay || currentDay <= nextMonthEndDay;
|
||||
}
|
||||
|
||||
if (!isInRange) {
|
||||
throw exception(TENANT_BALANCE_WITHDRAW_NOT_IN_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user