From d517682c62a5e79b694b0113c823848b673fe21c Mon Sep 17 00:00:00 2001 From: ziin Date: Fri, 26 Dec 2025 19:23:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(tenant-balance):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E4=BD=99=E9=A2=9D=E6=8F=90=E7=8E=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 /withdraw 接口,支持租户在限定日期内发起余额提现;增加冻结金额字段;补充提现相关错误码与日期校验逻辑。 --- .../TenantBalanceController.java | 8 ++ .../vo/TenantBalancePageReqVO.java | 5 +- .../tenantbalance/vo/TenantBalanceRespVO.java | 5 +- .../vo/TenantBalanceSaveReqVO.java | 4 + .../vo/TenantBalanceWithdrawReqVO.java | 24 +++++ .../tenantbalance/TenantBalanceDO.java | 4 + .../tenantbalance/TenantBalanceService.java | 6 ++ .../TenantBalanceServiceImpl.java | 93 ++++++++++++++++++- .../infra/enums/ErrorCodeConstants.java | 3 + 9 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceWithdrawReqVO.java diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/TenantBalanceController.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/TenantBalanceController.java index 4d3d340..845d1ce 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/TenantBalanceController.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/TenantBalanceController.java @@ -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 withdraw(@Valid @RequestBody TenantBalanceWithdrawReqVO withdrawReqVO) { + tenantBalanceService.withdraw(withdrawReqVO); + return success(true); + } + // ==================== 子表(租户积分记录) ==================== diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalancePageReqVO.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalancePageReqVO.java index 5fb5baa..c9dd051 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalancePageReqVO.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalancePageReqVO.java @@ -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; } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceRespVO.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceRespVO.java index 5419871..67a1bb8 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceRespVO.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceRespVO.java @@ -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; } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceSaveReqVO.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceSaveReqVO.java index c659ca6..1909d95 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceSaveReqVO.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceSaveReqVO.java @@ -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; } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceWithdrawReqVO.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceWithdrawReqVO.java new file mode 100644 index 0000000..f195c89 --- /dev/null +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceWithdrawReqVO.java @@ -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; +} diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalance/TenantBalanceDO.java b/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalance/TenantBalanceDO.java index f3bb9c9..35215ae 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalance/TenantBalanceDO.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalance/TenantBalanceDO.java @@ -44,5 +44,9 @@ public class TenantBalanceDO extends BaseDO { */ private LocalDateTime updatedAt; + /** + * 冻结金额 + */ + private BigDecimal frozenAmt; } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceService.java b/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceService.java index df9f78e..8995ee9 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceService.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceService.java @@ -129,5 +129,11 @@ public interface TenantBalanceService { */ TenantBalanceTransactionDO getTenantBalanceTransaction(Long id); + /** + * 租户提现 + * + * @param withdrawReqVO 提现信息 + */ + void withdraw(@Valid TenantBalanceWithdrawReqVO withdrawReqVO); } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceServiceImpl.java b/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceServiceImpl.java index 5161c91..f4b3c5d 100644 --- a/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceServiceImpl.java +++ b/keyboard-server/src/main/java/com/yolo/keyboard/service/tenantbalance/TenantBalanceServiceImpl.java @@ -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); + } + } + } \ No newline at end of file diff --git a/yolo-module-infra/src/main/java/com/yolo/keyboard/module/infra/enums/ErrorCodeConstants.java b/yolo-module-infra/src/main/java/com/yolo/keyboard/module/infra/enums/ErrorCodeConstants.java index fd443eb..4043207 100644 --- a/yolo-module-infra/src/main/java/com/yolo/keyboard/module/infra/enums/ErrorCodeConstants.java +++ b/yolo-module-infra/src/main/java/com/yolo/keyboard/module/infra/enums/ErrorCodeConstants.java @@ -87,6 +87,9 @@ public interface ErrorCodeConstants { ErrorCode USER_THEMES_NOT_EXISTS = new ErrorCode(1_001_202_013, "用户主题不存在"); ErrorCode TENANT_BALANCE_NOT_EXISTS = new ErrorCode(1_001_202_014, "租户余额不存在"); ErrorCode TENANT_BALANCE_TRANSACTION_NOT_EXISTS = new ErrorCode(1_001_202_015, "租户积分记录不存在"); + ErrorCode TENANT_BALANCE_WITHDRAW_NOT_IN_DATE = new ErrorCode(1_001_202_016, "当前不在提现日期范围内"); + ErrorCode TENANT_BALANCE_WITHDRAW_INSUFFICIENT = new ErrorCode(1_001_202_017, "余额不足,无法提现"); + ErrorCode TENANT_BALANCE_WITHDRAW_CONFIG_NOT_EXISTS = new ErrorCode(1_001_202_018, "提现配置不存在"); }