Compare commits
14 Commits
98e427c65a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a99f05d029 | |||
| bd315fcbd9 | |||
| 716cde6ea0 | |||
| 04755188d6 | |||
| b12f232f56 | |||
| d75a3e0212 | |||
| 299bc5e28b | |||
| d7ed10f45d | |||
| ca6e3d20f6 | |||
| 6fad3b45fe | |||
| a8da54c130 | |||
| d105bd4fa6 | |||
| be54601cdd | |||
| 2207add193 |
@@ -141,9 +141,10 @@ public class TenantBalanceController {
|
|||||||
@Operation(summary = "获得租户积分记录分页")
|
@Operation(summary = "获得租户积分记录分页")
|
||||||
@Parameter(name = "tenantId", description = "租户 Id")
|
@Parameter(name = "tenantId", description = "租户 Id")
|
||||||
@PreAuthorize("@ss.hasPermission('keyboard:tenant-balance:query')")
|
@PreAuthorize("@ss.hasPermission('keyboard:tenant-balance:query')")
|
||||||
public CommonResult<PageResult<TenantBalanceTransactionDO>> getTenantBalanceTransactionPage(PageParam pageReqVO,
|
public CommonResult<PageResult<TenantBalanceTransactionRespVO>> getTenantBalanceTransactionPage(PageParam pageReqVO,
|
||||||
@RequestParam("tenantId") Long tenantId) {
|
@RequestParam("tenantId") Long tenantId) {
|
||||||
return success(tenantBalanceService.getTenantBalanceTransactionPage(pageReqVO, tenantId));
|
PageResult<TenantBalanceTransactionDO> pageResult = tenantBalanceService.getTenantBalanceTransactionPage(pageReqVO, tenantId);
|
||||||
|
return success(BeanUtils.toBean(pageResult, TenantBalanceTransactionRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/tenant-balance-transaction/create")
|
@PostMapping("/tenant-balance-transaction/create")
|
||||||
@@ -183,7 +184,8 @@ public class TenantBalanceController {
|
|||||||
@Operation(summary = "获得租户积分记录")
|
@Operation(summary = "获得租户积分记录")
|
||||||
@Parameter(name = "id", description = "编号", required = true)
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
@PreAuthorize("@ss.hasPermission('keyboard:tenant-balance:query')")
|
@PreAuthorize("@ss.hasPermission('keyboard:tenant-balance:query')")
|
||||||
public CommonResult<TenantBalanceTransactionDO> getTenantBalanceTransaction(@RequestParam("id") Long id) {
|
public CommonResult<TenantBalanceTransactionRespVO> getTenantBalanceTransaction(@RequestParam("id") Long id) {
|
||||||
return success(tenantBalanceService.getTenantBalanceTransaction(id));
|
TenantBalanceTransactionDO transaction = tenantBalanceService.getTenantBalanceTransaction(id);
|
||||||
|
return success(BeanUtils.toBean(transaction, TenantBalanceTransactionRespVO.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR
|
|||||||
public class TenantBalancePageReqVO extends PageParam {
|
public class TenantBalancePageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "当前积分余额")
|
@Schema(description = "当前积分余额")
|
||||||
private Integer balance;
|
private BigDecimal balance;
|
||||||
|
|
||||||
@Schema(description = "乐观锁版本号")
|
@Schema(description = "乐观锁版本号")
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class TenantBalanceRespVO {
|
|||||||
|
|
||||||
@Schema(description = "当前积分余额", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "当前积分余额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@ExcelProperty("当前积分余额")
|
@ExcelProperty("当前积分余额")
|
||||||
private Integer balance;
|
private BigDecimal balance;
|
||||||
|
|
||||||
@Schema(description = "乐观锁版本号", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "乐观锁版本号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@ExcelProperty("乐观锁版本号")
|
@ExcelProperty("乐观锁版本号")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class TenantBalanceSaveReqVO {
|
|||||||
|
|
||||||
@Schema(description = "当前积分余额", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "当前积分余额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotNull(message = "当前积分余额不能为空")
|
@NotNull(message = "当前积分余额不能为空")
|
||||||
private Integer balance;
|
private BigDecimal balance;
|
||||||
|
|
||||||
@Schema(description = "乐观锁版本号", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "乐观锁版本号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotNull(message = "乐观锁版本号不能为空")
|
@NotNull(message = "乐观锁版本号不能为空")
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.yolo.keyboard.controller.admin.tenantbalance.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 租户积分记录 Response VO")
|
||||||
|
@Data
|
||||||
|
public class TenantBalanceTransactionRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "本次变动点数,正加负减", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
|
||||||
|
private BigDecimal points;
|
||||||
|
|
||||||
|
@Schema(description = "变动后余额快照", example = "1000.00")
|
||||||
|
private BigDecimal balance;
|
||||||
|
|
||||||
|
@Schema(description = "变动后冻结金额快照", example = "200.00")
|
||||||
|
private BigDecimal frozenAmt;
|
||||||
|
|
||||||
|
@Schema(description = "变动后可提现余额快照", example = "800.00")
|
||||||
|
private BigDecimal withdrawableBalance;
|
||||||
|
|
||||||
|
@Schema(description = "变动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "RECHARGE")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@Schema(description = "变动描述", example = "余额充值")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "订单Id/业务单号", example = "ORD123456")
|
||||||
|
private String orderId;
|
||||||
|
|
||||||
|
@Schema(description = "业务流水号", example = "BIZ123456")
|
||||||
|
private String bizNo;
|
||||||
|
|
||||||
|
@Schema(description = "操作人Id", example = "1")
|
||||||
|
private Long operatorId;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "管理员充值")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "租户Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,10 +38,12 @@ public class KeyboardTenantCommissionController {
|
|||||||
@Resource
|
@Resource
|
||||||
private KeyboardTenantCommissionService tenantCommissionService;
|
private KeyboardTenantCommissionService tenantCommissionService;
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建租户内购分成记录")
|
@Operation(summary = "创建租户内购分成记录")
|
||||||
@PreAuthorize("@ss.hasPermission('keyboard:tenant-commission:create')")
|
@PreAuthorize("@ss.hasPermission('keyboard:tenant-commission:create')")
|
||||||
public CommonResult<Long> createTenantCommission(@Valid @RequestBody KeyboardTenantCommissionSaveReqVO createReqVO) {
|
public CommonResult<Long> createTenantCommission(@Valid @RequestBody KeyboardTenantCommissionSaveReqVO createReqVO) {
|
||||||
|
|
||||||
return success(tenantCommissionService.createTenantCommission(createReqVO));
|
return success(tenantCommissionService.createTenantCommission(createReqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR
|
|||||||
@Data
|
@Data
|
||||||
public class KeyboardTenantCommissionPageReqVO extends PageParam {
|
public class KeyboardTenantCommissionPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "租户ID", example = "1", hidden = true)
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
@Schema(description = "内购记录ID", example = "20900")
|
@Schema(description = "内购记录ID", example = "20900")
|
||||||
private Integer purchaseRecordId;
|
private Integer purchaseRecordId;
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ public class KeyboardTenantWithdrawOrderController {
|
|||||||
@Operation(summary = "获得租户提现订单表(申请-审核-打款-完成/失败)分页")
|
@Operation(summary = "获得租户提现订单表(申请-审核-打款-完成/失败)分页")
|
||||||
@PreAuthorize("@ss.hasPermission('keyboard:tenant-withdraw-order:query')")
|
@PreAuthorize("@ss.hasPermission('keyboard:tenant-withdraw-order:query')")
|
||||||
public CommonResult<PageResult<KeyboardTenantWithdrawOrderRespVO>> getTenantWithdrawOrderPage(@Valid KeyboardTenantWithdrawOrderPageReqVO pageReqVO) {
|
public CommonResult<PageResult<KeyboardTenantWithdrawOrderRespVO>> getTenantWithdrawOrderPage(@Valid KeyboardTenantWithdrawOrderPageReqVO pageReqVO) {
|
||||||
PageResult<KeyboardTenantWithdrawOrderDO> pageResult = tenantWithdrawOrderService.getTenantWithdrawOrderPage(pageReqVO);
|
PageResult<KeyboardTenantWithdrawOrderRespVO> pageResult = tenantWithdrawOrderService.getTenantWithdrawOrderPage(pageReqVO);
|
||||||
return success(BeanUtils.toBean(pageResult, KeyboardTenantWithdrawOrderRespVO.class));
|
return success(pageResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export-excel")
|
@GetMapping("/export-excel")
|
||||||
@@ -95,10 +95,9 @@ public class KeyboardTenantWithdrawOrderController {
|
|||||||
public void exportTenantWithdrawOrderExcel(@Valid KeyboardTenantWithdrawOrderPageReqVO pageReqVO,
|
public void exportTenantWithdrawOrderExcel(@Valid KeyboardTenantWithdrawOrderPageReqVO pageReqVO,
|
||||||
HttpServletResponse response) throws IOException {
|
HttpServletResponse response) throws IOException {
|
||||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||||
List<KeyboardTenantWithdrawOrderDO> list = tenantWithdrawOrderService.getTenantWithdrawOrderPage(pageReqVO).getList();
|
List<KeyboardTenantWithdrawOrderRespVO> list = tenantWithdrawOrderService.getTenantWithdrawOrderPage(pageReqVO).getList();
|
||||||
// 导出 Excel
|
// 导出 Excel
|
||||||
ExcelUtils.write(response, "租户提现订单表(申请-审核-打款-完成/失败).xls", "数据", KeyboardTenantWithdrawOrderRespVO.class,
|
ExcelUtils.write(response, "租户提现订单表(申请-审核-打款-完成/失败).xls", "数据", KeyboardTenantWithdrawOrderRespVO.class, list);
|
||||||
BeanUtils.toBean(list, KeyboardTenantWithdrawOrderRespVO.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -111,4 +111,7 @@ public class KeyboardTenantWithdrawOrderPageReqVO extends PageParam {
|
|||||||
@Schema(description = "更新时间")
|
@Schema(description = "更新时间")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID列表(用于下级租户过滤)", hidden = true)
|
||||||
|
private List<Long> tenantIds;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -142,4 +142,12 @@ public class KeyboardTenantWithdrawOrderRespVO {
|
|||||||
@ExcelProperty("更新时间")
|
@ExcelProperty("更新时间")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Schema(description = "租户编号", example = "1")
|
||||||
|
@ExcelProperty("租户编号")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "租户名称", example = "芋道源码")
|
||||||
|
@ExcelProperty("租户名称")
|
||||||
|
private String tenantName;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -67,4 +67,8 @@ public class TenantBalanceTransactionDO {
|
|||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
private Long tenantId;
|
private Long tenantId;
|
||||||
|
|
||||||
|
private BigDecimal frozenAmt;
|
||||||
|
|
||||||
|
private BigDecimal withdrawableBalance;
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,19 @@ public interface KeyboardTenantCommissionMapper extends BaseMapperX<KeyboardTena
|
|||||||
return selectOne(KeyboardTenantCommissionDO::getPurchaseRecordId, purchaseRecordId);
|
return selectOne(KeyboardTenantCommissionDO::getPurchaseRecordId, purchaseRecordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据内购记录ID和租户ID查询分成记录
|
||||||
|
* 用于检查某个租户是否已经对某条内购记录计算过分成
|
||||||
|
*/
|
||||||
|
default KeyboardTenantCommissionDO selectByPurchaseRecordIdAndTenantId(Integer purchaseRecordId, Long tenantId) {
|
||||||
|
return selectOne(new LambdaQueryWrapperX<KeyboardTenantCommissionDO>()
|
||||||
|
.eq(KeyboardTenantCommissionDO::getPurchaseRecordId, purchaseRecordId)
|
||||||
|
.eq(KeyboardTenantCommissionDO::getTenantId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
default PageResult<KeyboardTenantCommissionDO> selectPage(KeyboardTenantCommissionPageReqVO reqVO) {
|
default PageResult<KeyboardTenantCommissionDO> selectPage(KeyboardTenantCommissionPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTenantCommissionDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTenantCommissionDO>()
|
||||||
|
.eqIfPresent(KeyboardTenantCommissionDO::getTenantId, reqVO.getTenantId())
|
||||||
.eqIfPresent(KeyboardTenantCommissionDO::getPurchaseRecordId, reqVO.getPurchaseRecordId())
|
.eqIfPresent(KeyboardTenantCommissionDO::getPurchaseRecordId, reqVO.getPurchaseRecordId())
|
||||||
.eqIfPresent(KeyboardTenantCommissionDO::getTransactionId, reqVO.getTransactionId())
|
.eqIfPresent(KeyboardTenantCommissionDO::getTransactionId, reqVO.getTransactionId())
|
||||||
.eqIfPresent(KeyboardTenantCommissionDO::getInviteeUserId, reqVO.getInviteeUserId())
|
.eqIfPresent(KeyboardTenantCommissionDO::getInviteeUserId, reqVO.getInviteeUserId())
|
||||||
@@ -47,5 +58,4 @@ public interface KeyboardTenantCommissionMapper extends BaseMapperX<KeyboardTena
|
|||||||
.eqIfPresent(KeyboardTenantCommissionDO::getRemark, reqVO.getRemark())
|
.eqIfPresent(KeyboardTenantCommissionDO::getRemark, reqVO.getRemark())
|
||||||
.orderByDesc(KeyboardTenantCommissionDO::getId));
|
.orderByDesc(KeyboardTenantCommissionDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public interface KeyboardTenantWithdrawOrderMapper extends BaseMapperX<KeyboardT
|
|||||||
|
|
||||||
default PageResult<KeyboardTenantWithdrawOrderDO> selectPage(KeyboardTenantWithdrawOrderPageReqVO reqVO) {
|
default PageResult<KeyboardTenantWithdrawOrderDO> selectPage(KeyboardTenantWithdrawOrderPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTenantWithdrawOrderDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTenantWithdrawOrderDO>()
|
||||||
|
.inIfPresent(KeyboardTenantWithdrawOrderDO::getTenantId, reqVO.getTenantIds())
|
||||||
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getWithdrawNo, reqVO.getWithdrawNo())
|
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getWithdrawNo, reqVO.getWithdrawNo())
|
||||||
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getBizNo, reqVO.getBizNo())
|
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getBizNo, reqVO.getBizNo())
|
||||||
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getCurrency, reqVO.getCurrency())
|
.eqIfPresent(KeyboardTenantWithdrawOrderDO::getCurrency, reqVO.getCurrency())
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ public class CommissionWithdrawableJob implements JobHandler {
|
|||||||
.bizNo(bizNo)
|
.bizNo(bizNo)
|
||||||
.points(tenantTotalAmount)
|
.points(tenantTotalAmount)
|
||||||
.balance(balance.getBalance())
|
.balance(balance.getBalance())
|
||||||
|
.frozenAmt(balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO)
|
||||||
|
.withdrawableBalance(newWithdrawable)
|
||||||
.tenantId(tenantId)
|
.tenantId(tenantId)
|
||||||
.type(WITHDRAWABLE_TYPE)
|
.type(WITHDRAWABLE_TYPE)
|
||||||
.description("分成转可提现")
|
.description("分成转可提现")
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 租户分成计算定时任务
|
* 租户分成计算定时任务
|
||||||
* 每小时执行一次,计算邀请用户的内购分成
|
* 每小时执行一次,计算邀请用户的内购分成
|
||||||
|
* 支持一级代理和二级代理的分成计算
|
||||||
*
|
*
|
||||||
* @author ziin
|
* @author ziin
|
||||||
*/
|
*/
|
||||||
@@ -110,42 +111,142 @@ public class TenantCommissionCalculateJob implements JobHandler {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取收益归属租户
|
// 获取收益归属租户(邀请人所属租户)
|
||||||
Long tenantId = invite.getProfitTenantId();
|
Long inviterTenantId = invite.getProfitTenantId();
|
||||||
if (tenantId == null) {
|
if (inviterTenantId == null) {
|
||||||
tenantId = invite.getInviterTenantId();
|
inviterTenantId = invite.getInviterTenantId();
|
||||||
}
|
}
|
||||||
if (tenantId == null) {
|
if (inviterTenantId == null) {
|
||||||
log.warn("[TenantCommissionCalculateJob] 用户 {} 的邀请关系没有关联租户,跳过", record.getUserId());
|
log.warn("[TenantCommissionCalculateJob] 用户 {} 的邀请关系没有关联租户,跳过", record.getUserId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取租户信息和分成比例
|
// 获取邀请人租户信息
|
||||||
TenantDO tenant = tenantMapper.selectById(tenantId);
|
TenantDO inviterTenant = tenantMapper.selectById(inviterTenantId);
|
||||||
if (tenant == null) {
|
if (inviterTenant == null) {
|
||||||
log.warn("[TenantCommissionCalculateJob] 租户 {} 不存在,跳过", tenantId);
|
log.warn("[TenantCommissionCalculateJob] 租户 {} 不存在,跳过", inviterTenantId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal commissionRate = tenant.getProfitShareRatio();
|
// 获取内购金额
|
||||||
if (commissionRate == null || commissionRate.compareTo(BigDecimal.ZERO) <= 0) {
|
|
||||||
log.debug("[TenantCommissionCalculateJob] 租户 {} 没有设置分成比例,跳过", tenantId);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 计算分成金额
|
|
||||||
BigDecimal purchaseAmount = record.getPrice();
|
BigDecimal purchaseAmount = record.getPrice();
|
||||||
if (purchaseAmount == null || purchaseAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (purchaseAmount == null || purchaseAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
log.debug("[TenantCommissionCalculateJob] 内购记录 {} 金额无效,跳过", record.getId());
|
log.debug("[TenantCommissionCalculateJob] 内购记录 {} 金额无效,跳过", record.getId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
LocalDateTime withdrawableAt = now.plusDays(30);
|
||||||
|
|
||||||
|
// 判断是否是二级代理(有上级租户)
|
||||||
|
if (inviterTenant.getParentId() != null) {
|
||||||
|
// 二级代理场景:需要给一级代理和二级代理都分成
|
||||||
|
TenantDO parentTenant = tenantMapper.selectById(inviterTenant.getParentId());
|
||||||
|
if (parentTenant == null) {
|
||||||
|
log.warn("[TenantCommissionCalculateJob] 上级租户 {} 不存在,跳过", inviterTenant.getParentId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用一级代理的分成比例计算总分成
|
||||||
|
BigDecimal totalCommissionRate = parentTenant.getProfitShareRatio();
|
||||||
|
if (totalCommissionRate == null || totalCommissionRate.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
log.debug("[TenantCommissionCalculateJob] 一级代理租户 {} 没有设置分成比例,跳过", parentTenant.getId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalCommissionAmount = purchaseAmount.multiply(totalCommissionRate)
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
// 获取二级代理的返点比例
|
||||||
|
BigDecimal rebateRatio = inviterTenant.getUpstreamRebateRatio();
|
||||||
|
if (rebateRatio == null || rebateRatio.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
// 如果没有设置返点比例,全部归一级代理
|
||||||
|
log.debug("[TenantCommissionCalculateJob] 二级代理租户 {} 没有设置返点比例,全部归一级代理", inviterTenantId);
|
||||||
|
int count = createCommissionRecord(record, invite, parentTenant.getId(), purchaseAmount,
|
||||||
|
totalCommissionRate, totalCommissionAmount, now, withdrawableAt,
|
||||||
|
"一级代理分成(二级代理无返点)");
|
||||||
|
commissionCount += count;
|
||||||
|
totalCommission = totalCommission.add(totalCommissionAmount);
|
||||||
|
} else {
|
||||||
|
// 计算二级代理分成
|
||||||
|
BigDecimal secondLevelAmount = totalCommissionAmount.multiply(rebateRatio)
|
||||||
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
// 计算一级代理分成
|
||||||
|
BigDecimal firstLevelAmount = totalCommissionAmount.subtract(secondLevelAmount);
|
||||||
|
|
||||||
|
// 为二级代理创建分成记录
|
||||||
|
if (secondLevelAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
int count = createCommissionRecord(record, invite, inviterTenantId, purchaseAmount,
|
||||||
|
rebateRatio, secondLevelAmount, now, withdrawableAt,
|
||||||
|
"二级代理分成");
|
||||||
|
commissionCount += count;
|
||||||
|
totalCommission = totalCommission.add(secondLevelAmount);
|
||||||
|
log.info("[TenantCommissionCalculateJob] 内购记录 {}, 二级代理 {}, 分成金额 {}",
|
||||||
|
record.getId(), inviterTenantId, secondLevelAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为一级代理创建分成记录
|
||||||
|
if (firstLevelAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
BigDecimal firstLevelRate = BigDecimal.ONE.subtract(rebateRatio);
|
||||||
|
int count = createCommissionRecord(record, invite, parentTenant.getId(), purchaseAmount,
|
||||||
|
firstLevelRate, firstLevelAmount, now, withdrawableAt,
|
||||||
|
"一级代理分成(扣除二级返点)");
|
||||||
|
commissionCount += count;
|
||||||
|
totalCommission = totalCommission.add(firstLevelAmount);
|
||||||
|
log.info("[TenantCommissionCalculateJob] 内购记录 {}, 一级代理 {}, 分成金额 {}",
|
||||||
|
record.getId(), parentTenant.getId(), firstLevelAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 一级代理场景:全部分成归一级代理
|
||||||
|
BigDecimal commissionRate = inviterTenant.getProfitShareRatio();
|
||||||
|
if (commissionRate == null || commissionRate.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
log.debug("[TenantCommissionCalculateJob] 租户 {} 没有设置分成比例,跳过", inviterTenantId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
BigDecimal commissionAmount = purchaseAmount.multiply(commissionRate)
|
BigDecimal commissionAmount = purchaseAmount.multiply(commissionRate)
|
||||||
.setScale(2, RoundingMode.HALF_UP);
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
// 4. 创建分成记录
|
int count = createCommissionRecord(record, invite, inviterTenantId, purchaseAmount,
|
||||||
LocalDateTime now = LocalDateTime.now();
|
commissionRate, commissionAmount, now, withdrawableAt,
|
||||||
LocalDateTime withdrawableAt = now.plusDays(30); // 30天后可提现
|
"一级代理分成");
|
||||||
|
commissionCount += count;
|
||||||
|
totalCommission = totalCommission.add(commissionAmount);
|
||||||
|
|
||||||
|
log.info("[TenantCommissionCalculateJob] 内购记录 {}, 一级代理 {}, 分成金额 {}",
|
||||||
|
record.getId(), inviterTenantId, commissionAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = String.format("处理内购记录 %d 条,生成分成 %d 条,总分成金额 %s",
|
||||||
|
processedCount, commissionCount, totalCommission.toPlainString());
|
||||||
|
log.info("[TenantCommissionCalculateJob] 任务执行完成: {}", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建分成记录并更新租户余额
|
||||||
|
*
|
||||||
|
* @return 创建的分成记录数量
|
||||||
|
*/
|
||||||
|
private int createCommissionRecord(KeyboardUserPurchaseRecordsDO record,
|
||||||
|
KeyboardUserInvitesDO invite,
|
||||||
|
Long tenantId,
|
||||||
|
BigDecimal purchaseAmount,
|
||||||
|
BigDecimal commissionRate,
|
||||||
|
BigDecimal commissionAmount,
|
||||||
|
LocalDateTime now,
|
||||||
|
LocalDateTime withdrawableAt,
|
||||||
|
String remark) {
|
||||||
|
// 0. 检查该租户是否已对该内购记录计算过分成(防止重复计算)
|
||||||
|
KeyboardTenantCommissionDO existingCommission = commissionMapper.selectByPurchaseRecordIdAndTenantId(record.getId(), tenantId);
|
||||||
|
if (existingCommission != null) {
|
||||||
|
log.debug("[TenantCommissionCalculateJob] 租户 {} 已对内购记录 {} 计算过分成,跳过", tenantId, record.getId());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 创建分成记录
|
||||||
KeyboardTenantCommissionDO commission = KeyboardTenantCommissionDO.builder()
|
KeyboardTenantCommissionDO commission = KeyboardTenantCommissionDO.builder()
|
||||||
.purchaseRecordId(record.getId())
|
.purchaseRecordId(record.getId())
|
||||||
.transactionId(record.getTransactionId())
|
.transactionId(record.getTransactionId())
|
||||||
@@ -162,12 +263,12 @@ public class TenantCommissionCalculateJob implements JobHandler {
|
|||||||
.withdrawableProcessed(false)
|
.withdrawableProcessed(false)
|
||||||
.createdAt(now)
|
.createdAt(now)
|
||||||
.updatedAt(now)
|
.updatedAt(now)
|
||||||
|
.remark(remark)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 5. 更新租户余额(分成金额先计入总余额,30天后才可提现)
|
// 2. 更新租户余额(分成金额先计入总余额,30天后才可提现)
|
||||||
TenantBalanceDO balance = tenantBalanceMapper.selectById(tenantId);
|
TenantBalanceDO balance = tenantBalanceMapper.selectById(tenantId);
|
||||||
if (balance == null) {
|
if (balance == null) {
|
||||||
// 如果租户余额记录不存在,创建一个
|
|
||||||
balance = new TenantBalanceDO();
|
balance = new TenantBalanceDO();
|
||||||
balance.setId(tenantId);
|
balance.setId(tenantId);
|
||||||
balance.setBalance(commissionAmount);
|
balance.setBalance(commissionAmount);
|
||||||
@@ -181,35 +282,27 @@ public class TenantCommissionCalculateJob implements JobHandler {
|
|||||||
tenantBalanceMapper.updateById(balance);
|
tenantBalanceMapper.updateById(balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 创建余额交易记录
|
// 3. 创建余额交易记录
|
||||||
String bizNo = BizNoGenerator.generate("COMM");
|
String bizNo = BizNoGenerator.generate("COMM");
|
||||||
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
||||||
.bizNo(bizNo)
|
.bizNo(bizNo)
|
||||||
.points(commissionAmount)
|
.points(commissionAmount)
|
||||||
.balance(balance.getBalance())
|
.balance(balance.getBalance())
|
||||||
|
.frozenAmt(balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO)
|
||||||
|
.withdrawableBalance(balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO)
|
||||||
.tenantId(tenantId)
|
.tenantId(tenantId)
|
||||||
.type(COMMISSION_TYPE)
|
.type(COMMISSION_TYPE)
|
||||||
.description("邀请用户内购分成")
|
.description("邀请用户内购分成")
|
||||||
.orderId(record.getTransactionId())
|
.orderId(record.getTransactionId())
|
||||||
.createdAt(now)
|
.createdAt(now)
|
||||||
.remark("内购记录ID: " + record.getId() + ", 被邀请用户: " + record.getUserId())
|
.remark("内购记录ID: " + record.getId() + ", 被邀请用户: " + record.getUserId() + ", " + remark)
|
||||||
.build();
|
.build();
|
||||||
balanceTransactionMapper.insert(transaction);
|
balanceTransactionMapper.insert(transaction);
|
||||||
|
|
||||||
// 更新分成记录的关联交易ID
|
// 4. 更新分成记录的关联交易ID并保存
|
||||||
commission.setBalanceTransactionId(transaction.getId());
|
commission.setBalanceTransactionId(transaction.getId());
|
||||||
commissionMapper.insert(commission);
|
commissionMapper.insert(commission);
|
||||||
|
|
||||||
commissionCount++;
|
return 1;
|
||||||
totalCommission = totalCommission.add(commissionAmount);
|
|
||||||
|
|
||||||
log.info("[TenantCommissionCalculateJob] 处理内购记录 {}, 租户 {}, 分成金额 {}",
|
|
||||||
record.getId(), tenantId, commissionAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
String result = String.format("处理内购记录 %d 条,生成分成 %d 条,总分成金额 %s",
|
|
||||||
processedCount, commissionCount, totalCommission.toPlainString());
|
|
||||||
log.info("[TenantCommissionCalculateJob] 任务执行完成: {}", result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
.bizNo(BizNoGenerator.generate("RECHARGE")) // 生成充值业务编号
|
.bizNo(BizNoGenerator.generate("RECHARGE")) // 生成充值业务编号
|
||||||
.points(new BigDecimal(String.valueOf(addReqVO.getAmount()))) // 充值金额
|
.points(new BigDecimal(String.valueOf(addReqVO.getAmount()))) // 充值金额
|
||||||
.balance(newBalance) // 充值后余额
|
.balance(newBalance) // 充值后余额
|
||||||
|
.frozenAmt(balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO) // 当前冻结金额
|
||||||
|
.withdrawableBalance(balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO) // 当前可提现金额
|
||||||
.tenantId(addReqVO.getId())
|
.tenantId(addReqVO.getId())
|
||||||
.type("RECHARGE") // 交易类型:充值
|
.type("RECHARGE") // 交易类型:充值
|
||||||
.description("余额充值") // 交易描述
|
.description("余额充值") // 交易描述
|
||||||
@@ -243,18 +245,18 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
throw exception(TENANT_BALANCE_NOT_EXISTS);
|
throw exception(TENANT_BALANCE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 校验可用余额是否充足(可用余额 = 余额 - 冻结金额)
|
// 3. 校验可提现金额是否充足
|
||||||
BigDecimal withdrawAmount = withdrawReqVO.getAmount();
|
BigDecimal withdrawAmount = withdrawReqVO.getAmount();
|
||||||
BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
|
BigDecimal withdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
|
||||||
BigDecimal availableBalance = balance.getBalance().subtract(frozenAmt);
|
if (withdrawableBalance.compareTo(withdrawAmount) < 0) {
|
||||||
if (availableBalance.compareTo(withdrawAmount) < 0) {
|
|
||||||
throw exception(TENANT_BALANCE_WITHDRAW_INSUFFICIENT);
|
throw exception(TENANT_BALANCE_WITHDRAW_INSUFFICIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 扣减余额并增加冻结金额
|
// 4. 从可提现金额中扣减并增加冻结金额
|
||||||
BigDecimal newBalance = balance.getBalance().subtract(withdrawAmount);
|
BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
|
||||||
|
BigDecimal newWithdrawableBalance = withdrawableBalance.subtract(withdrawAmount);
|
||||||
BigDecimal newFrozenAmt = frozenAmt.add(withdrawAmount);
|
BigDecimal newFrozenAmt = frozenAmt.add(withdrawAmount);
|
||||||
balance.setBalance(newBalance);
|
balance.setWithdrawableBalance(newWithdrawableBalance);
|
||||||
balance.setFrozenAmt(newFrozenAmt);
|
balance.setFrozenAmt(newFrozenAmt);
|
||||||
int updateCount = tenantBalanceMapper.updateById(balance);
|
int updateCount = tenantBalanceMapper.updateById(balance);
|
||||||
if (updateCount == 0) {
|
if (updateCount == 0) {
|
||||||
@@ -269,7 +271,9 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
||||||
.bizNo(bizNo)
|
.bizNo(bizNo)
|
||||||
.points(withdrawAmount.negate()) // 冻结金额(负数表示冻结扣减)
|
.points(withdrawAmount.negate()) // 冻结金额(负数表示冻结扣减)
|
||||||
.balance(newBalance) // 扣减后的余额
|
.balance(balance.getBalance()) // 当前总余额
|
||||||
|
.frozenAmt(newFrozenAmt) // 冻结后的冻结金额
|
||||||
|
.withdrawableBalance(newWithdrawableBalance) // 扣减后的可提现余额
|
||||||
.tenantId(tenantId)
|
.tenantId(tenantId)
|
||||||
.type("FREEZE")
|
.type("FREEZE")
|
||||||
.description("提现冻结")
|
.description("提现冻结")
|
||||||
|
|||||||
@@ -59,4 +59,11 @@ public interface KeyboardTenantCommissionService {
|
|||||||
*/
|
*/
|
||||||
PageResult<KeyboardTenantCommissionDO> getTenantCommissionPage(KeyboardTenantCommissionPageReqVO pageReqVO);
|
PageResult<KeyboardTenantCommissionDO> getTenantCommissionPage(KeyboardTenantCommissionPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前登录租户的分成记录分页
|
||||||
|
*
|
||||||
|
* @param pageReqVO 分页查询
|
||||||
|
* @return 租户内购分成记录分页
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,9 @@ import com.yolo.keyboard.framework.common.pojo.PageParam;
|
|||||||
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
|
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
|
||||||
|
|
||||||
import com.yolo.keyboard.dal.mysql.tenantcommission.KeyboardTenantCommissionMapper;
|
import com.yolo.keyboard.dal.mysql.tenantcommission.KeyboardTenantCommissionMapper;
|
||||||
|
import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder;
|
||||||
|
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
|
||||||
|
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.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.convertList;
|
||||||
@@ -31,6 +34,9 @@ public class KeyboardTenantCommissionServiceImpl implements KeyboardTenantCommis
|
|||||||
@Resource
|
@Resource
|
||||||
private KeyboardTenantCommissionMapper tenantCommissionMapper;
|
private KeyboardTenantCommissionMapper tenantCommissionMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TenantMapper tenantMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createTenantCommission(KeyboardTenantCommissionSaveReqVO createReqVO) {
|
public Long createTenantCommission(KeyboardTenantCommissionSaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
@@ -78,6 +84,15 @@ public class KeyboardTenantCommissionServiceImpl implements KeyboardTenantCommis
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<KeyboardTenantCommissionDO> getTenantCommissionPage(KeyboardTenantCommissionPageReqVO pageReqVO) {
|
public PageResult<KeyboardTenantCommissionDO> getTenantCommissionPage(KeyboardTenantCommissionPageReqVO pageReqVO) {
|
||||||
|
// 如果当前租户的 tenantLevel 不等于 0,只能查询属于自己的数据
|
||||||
|
Long currentTenantId = TenantContextHolder.getTenantId();
|
||||||
|
if (currentTenantId != null) {
|
||||||
|
TenantDO currentTenant = tenantMapper.selectById(currentTenantId);
|
||||||
|
if (currentTenant != null && currentTenant.getTenantLevel() != null
|
||||||
|
&& currentTenant.getTenantLevel() != 0) {
|
||||||
|
pageReqVO.setTenantId(currentTenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
return tenantCommissionMapper.selectPage(pageReqVO);
|
return tenantCommissionMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,6 @@ public interface KeyboardTenantWithdrawOrderService {
|
|||||||
* @param pageReqVO 分页查询
|
* @param pageReqVO 分页查询
|
||||||
* @return 租户提现订单表(申请-审核-打款-完成/失败)分页
|
* @return 租户提现订单表(申请-审核-打款-完成/失败)分页
|
||||||
*/
|
*/
|
||||||
PageResult<KeyboardTenantWithdrawOrderDO> getTenantWithdrawOrderPage(KeyboardTenantWithdrawOrderPageReqVO pageReqVO);
|
PageResult<KeyboardTenantWithdrawOrderRespVO> getTenantWithdrawOrderPage(KeyboardTenantWithdrawOrderPageReqVO pageReqVO);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,34 @@
|
|||||||
package com.yolo.keyboard.service.tenantwithdraworder;
|
package com.yolo.keyboard.service.tenantwithdraworder;
|
||||||
|
|
||||||
|
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderPageReqVO;
|
||||||
|
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderRespVO;
|
||||||
|
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.KeyboardTenantWithdrawOrderSaveReqVO;
|
||||||
|
import com.yolo.keyboard.dal.dataobject.tenantbalance.TenantBalanceDO;
|
||||||
|
import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO;
|
||||||
|
import com.yolo.keyboard.dal.dataobject.tenantwithdraworder.KeyboardTenantWithdrawOrderDO;
|
||||||
|
import com.yolo.keyboard.dal.mysql.tenantbalance.TenantBalanceMapper;
|
||||||
|
import com.yolo.keyboard.dal.mysql.tenantbalancetransaction.TenantBalanceTransactionMapper;
|
||||||
|
import com.yolo.keyboard.dal.mysql.tenantwithdraworder.KeyboardTenantWithdrawOrderMapper;
|
||||||
|
import com.yolo.keyboard.framework.common.pojo.PageResult;
|
||||||
|
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
|
||||||
|
import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder;
|
||||||
|
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
|
||||||
|
import com.yolo.keyboard.module.system.dal.mysql.tenant.TenantMapper;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import org.springframework.stereotype.Service;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.*;
|
import java.math.BigDecimal;
|
||||||
import com.yolo.keyboard.controller.admin.tenantwithdraworder.vo.*;
|
import java.time.LocalDateTime;
|
||||||
import com.yolo.keyboard.dal.dataobject.tenantwithdraworder.KeyboardTenantWithdrawOrderDO;
|
import java.util.HashMap;
|
||||||
import com.yolo.keyboard.framework.common.pojo.PageResult;
|
import java.util.List;
|
||||||
import com.yolo.keyboard.framework.common.pojo.PageParam;
|
import java.util.Map;
|
||||||
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.yolo.keyboard.dal.mysql.tenantwithdraworder.KeyboardTenantWithdrawOrderMapper;
|
|
||||||
|
|
||||||
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
|
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_WITHDRAW_ORDER_NOT_EXISTS;
|
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TENANT_WITHDRAW_ORDER_NOT_EXISTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +43,15 @@ public class KeyboardTenantWithdrawOrderServiceImpl implements KeyboardTenantWit
|
|||||||
@Resource
|
@Resource
|
||||||
private KeyboardTenantWithdrawOrderMapper tenantWithdrawOrderMapper;
|
private KeyboardTenantWithdrawOrderMapper tenantWithdrawOrderMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TenantMapper tenantMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TenantBalanceMapper tenantBalanceMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TenantBalanceTransactionMapper tenantBalanceTransactionMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO createReqVO) {
|
public Long createTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
@@ -43,12 +63,145 @@ public class KeyboardTenantWithdrawOrderServiceImpl implements KeyboardTenantWit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void updateTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO updateReqVO) {
|
public void updateTenantWithdrawOrder(KeyboardTenantWithdrawOrderSaveReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateTenantWithdrawOrderExists(updateReqVO.getId());
|
KeyboardTenantWithdrawOrderDO existingOrder = tenantWithdrawOrderMapper.selectById(updateReqVO.getId());
|
||||||
// 更新
|
if (existingOrder == null) {
|
||||||
|
throw exception(TENANT_WITHDRAW_ORDER_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新订单
|
||||||
KeyboardTenantWithdrawOrderDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardTenantWithdrawOrderDO.class);
|
KeyboardTenantWithdrawOrderDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardTenantWithdrawOrderDO.class);
|
||||||
tenantWithdrawOrderMapper.updateById(updateObj);
|
tenantWithdrawOrderMapper.updateById(updateObj);
|
||||||
|
|
||||||
|
String newStatus = updateReqVO.getStatus();
|
||||||
|
String oldStatus = existingOrder.getStatus();
|
||||||
|
|
||||||
|
// 如果提现状态更新为成功(PAID),则扣除提现用户的冻结金额并创建流水记录
|
||||||
|
if ("PAID".equals(newStatus) && !"PAID".equals(oldStatus)) {
|
||||||
|
handleWithdrawSuccess(existingOrder);
|
||||||
|
}
|
||||||
|
// 如果提现状态更新为已拒绝、已取消、打款失败,则返还冻结金额到可提现余额并创建流水记录
|
||||||
|
else if (isRefundStatus(newStatus) && !isRefundStatus(oldStatus)) {
|
||||||
|
handleWithdrawRefund(existingOrder, newStatus, updateReqVO.getReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为需要退还冻结金额的状态
|
||||||
|
*/
|
||||||
|
private boolean isRefundStatus(String status) {
|
||||||
|
return "REJECTED".equals(status) || "CANCELED".equals(status) || "FAILED".equals(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理提现成功:扣除冻结金额并记录流水
|
||||||
|
*/
|
||||||
|
private void handleWithdrawSuccess(KeyboardTenantWithdrawOrderDO order) {
|
||||||
|
TenantBalanceDO balance = tenantBalanceMapper.selectById(order.getTenantId());
|
||||||
|
if (balance == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣除冻结金额
|
||||||
|
BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
|
||||||
|
BigDecimal withdrawAmount = order.getAmount();
|
||||||
|
BigDecimal newFrozenAmt = frozenAmt.subtract(withdrawAmount);
|
||||||
|
if (newFrozenAmt.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
newFrozenAmt = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
balance.setFrozenAmt(newFrozenAmt);
|
||||||
|
tenantBalanceMapper.updateById(balance);
|
||||||
|
|
||||||
|
// 创建提现成功的流水记录
|
||||||
|
BigDecimal currentWithdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
|
||||||
|
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
||||||
|
.bizNo(order.getBizNo())
|
||||||
|
.points(withdrawAmount.negate()) // 提现金额(负数表示支出)
|
||||||
|
.balance(balance.getBalance()) // 当前总余额
|
||||||
|
.frozenAmt(newFrozenAmt) // 扣除后的冻结金额
|
||||||
|
.withdrawableBalance(currentWithdrawableBalance) // 当前可提现余额
|
||||||
|
.tenantId(order.getTenantId())
|
||||||
|
.type("WITHDRAW_SUCCESS")
|
||||||
|
.description("提现成功")
|
||||||
|
.orderId(order.getWithdrawNo())
|
||||||
|
.operatorId(TenantContextHolder.getTenantId())
|
||||||
|
.createdAt(LocalDateTime.now())
|
||||||
|
.build();
|
||||||
|
tenantBalanceTransactionMapper.insert(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理提现退还:返还冻结金额到可提现余额并记录流水
|
||||||
|
*
|
||||||
|
* @param order 提现订单
|
||||||
|
* @param newStatus 新状态
|
||||||
|
* @param reason 拒绝/失败/取消原因
|
||||||
|
*/
|
||||||
|
private void handleWithdrawRefund(KeyboardTenantWithdrawOrderDO order, String newStatus, String reason) {
|
||||||
|
TenantBalanceDO balance = tenantBalanceMapper.selectById(order.getTenantId());
|
||||||
|
if (balance == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal withdrawAmount = order.getAmount();
|
||||||
|
|
||||||
|
// 扣除冻结金额
|
||||||
|
BigDecimal frozenAmt = balance.getFrozenAmt() != null ? balance.getFrozenAmt() : BigDecimal.ZERO;
|
||||||
|
BigDecimal newFrozenAmt = frozenAmt.subtract(withdrawAmount);
|
||||||
|
if (newFrozenAmt.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
newFrozenAmt = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
balance.setFrozenAmt(newFrozenAmt);
|
||||||
|
|
||||||
|
// 返还到可提现余额
|
||||||
|
BigDecimal withdrawableBalance = balance.getWithdrawableBalance() != null ? balance.getWithdrawableBalance() : BigDecimal.ZERO;
|
||||||
|
BigDecimal newWithdrawableBalance = withdrawableBalance.add(withdrawAmount);
|
||||||
|
balance.setWithdrawableBalance(newWithdrawableBalance);
|
||||||
|
|
||||||
|
tenantBalanceMapper.updateById(balance);
|
||||||
|
|
||||||
|
// 根据状态确定流水类型和描述
|
||||||
|
String type;
|
||||||
|
String description;
|
||||||
|
switch (newStatus) {
|
||||||
|
case "REJECTED":
|
||||||
|
type = "WITHDRAW_REJECTED";
|
||||||
|
description = "提现被拒绝,金额已退还";
|
||||||
|
break;
|
||||||
|
case "CANCELED":
|
||||||
|
type = "WITHDRAW_CANCELED";
|
||||||
|
description = "提现已取消,金额已退还";
|
||||||
|
break;
|
||||||
|
case "FAILED":
|
||||||
|
type = "WITHDRAW_FAILED";
|
||||||
|
description = "提现打款失败,金额已退还";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = "WITHDRAW_REFUND";
|
||||||
|
description = "提现退还";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备注:优先使用传入的原因,如果没有则使用默认描述
|
||||||
|
String remark = (reason != null && !reason.trim().isEmpty()) ? reason : description;
|
||||||
|
|
||||||
|
// 创建退还流水记录
|
||||||
|
TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder()
|
||||||
|
.bizNo(order.getBizNo())
|
||||||
|
.points(withdrawAmount) // 退还金额(正数表示收入)
|
||||||
|
.balance(balance.getBalance()) // 当前总余额
|
||||||
|
.frozenAmt(newFrozenAmt) // 扣除后的冻结金额
|
||||||
|
.withdrawableBalance(newWithdrawableBalance) // 退还后的可提现余额
|
||||||
|
.tenantId(order.getTenantId())
|
||||||
|
.type(type)
|
||||||
|
.description(description)
|
||||||
|
.remark(remark)
|
||||||
|
.orderId(order.getWithdrawNo())
|
||||||
|
.operatorId(TenantContextHolder.getTenantId())
|
||||||
|
.createdAt(LocalDateTime.now())
|
||||||
|
.build();
|
||||||
|
tenantBalanceTransactionMapper.insert(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,8 +231,67 @@ public class KeyboardTenantWithdrawOrderServiceImpl implements KeyboardTenantWit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<KeyboardTenantWithdrawOrderDO> getTenantWithdrawOrderPage(KeyboardTenantWithdrawOrderPageReqVO pageReqVO) {
|
public PageResult<KeyboardTenantWithdrawOrderRespVO> getTenantWithdrawOrderPage(KeyboardTenantWithdrawOrderPageReqVO pageReqVO) {
|
||||||
return tenantWithdrawOrderMapper.selectPage(pageReqVO);
|
// 根据当前租户级别过滤下级租户的提现申请
|
||||||
|
Long currentTenantId = TenantContextHolder.getTenantId();
|
||||||
|
if (currentTenantId != null) {
|
||||||
|
TenantDO currentTenant = tenantMapper.selectById(currentTenantId);
|
||||||
|
if (currentTenant != null && currentTenant.getTenantLevel() != null) {
|
||||||
|
if (currentTenant.getTenantLevel() == 0) {
|
||||||
|
// 系统管理员:只能查看1级代理的提现申请
|
||||||
|
List<TenantDO> firstLevelAgents = tenantMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TenantDO>().eq(TenantDO::getTenantLevel, 1));
|
||||||
|
List<Long> firstLevelAgentIds = firstLevelAgents.stream()
|
||||||
|
.map(TenantDO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollUtil.isEmpty(firstLevelAgentIds)) {
|
||||||
|
// 没有1级代理,返回空结果
|
||||||
|
return PageResult.empty(0L);
|
||||||
|
}
|
||||||
|
pageReqVO.setTenantIds(firstLevelAgentIds);
|
||||||
|
} else {
|
||||||
|
// 非系统管理员:只能查看直属下级租户的提现申请
|
||||||
|
List<TenantDO> subordinateTenants = tenantMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TenantDO>().eq(TenantDO::getParentId, currentTenantId));
|
||||||
|
List<Long> subordinateTenantIds = subordinateTenants.stream()
|
||||||
|
.map(TenantDO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollUtil.isEmpty(subordinateTenantIds)) {
|
||||||
|
// 没有下级租户,返回空结果
|
||||||
|
return PageResult.empty(0L);
|
||||||
|
}
|
||||||
|
pageReqVO.setTenantIds(subordinateTenantIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页查询租户提现订单数据
|
||||||
|
PageResult<KeyboardTenantWithdrawOrderDO> pageResult = tenantWithdrawOrderMapper.selectPage(pageReqVO);
|
||||||
|
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||||
|
return PageResult.empty(pageResult.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量获取租户名称 - 提升性能,避免N+1查询
|
||||||
|
List<Long> tenantIds = pageResult.getList().stream()
|
||||||
|
.map(KeyboardTenantWithdrawOrderDO::getTenantId)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<TenantDO> tenants = tenantMapper.selectBatchIds(tenantIds);
|
||||||
|
Map<Long, String> tenantNameMap = CollUtil.isEmpty(tenants)
|
||||||
|
? new HashMap<>() // 如果没有查询到租户数据,返回空map
|
||||||
|
: tenants.stream().collect(Collectors.toMap(TenantDO::getId, TenantDO::getName, (a, b) -> a)); // 构建租户ID到名称的映射
|
||||||
|
|
||||||
|
// 转换为 VO 并填充租户名称
|
||||||
|
List<KeyboardTenantWithdrawOrderRespVO> voList = pageResult.getList().stream().map(order -> {
|
||||||
|
// 将DO转换为VO
|
||||||
|
KeyboardTenantWithdrawOrderRespVO vo = BeanUtils.toBean(order, KeyboardTenantWithdrawOrderRespVO.class);
|
||||||
|
// 根据租户ID获取并设置租户名称
|
||||||
|
vo.setTenantName(tenantNameMap.get(order.getTenantId()));
|
||||||
|
return vo;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 返回包含VO列表和总数的分页结果
|
||||||
|
return new PageResult<>(voList, pageResult.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
|
|||||||
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
|
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
|
||||||
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
|
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
|
||||||
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
|
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
|
||||||
|
import com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant.TenantInfoRespVO;
|
||||||
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
|
import com.yolo.keyboard.module.system.dal.dataobject.tenant.TenantDO;
|
||||||
import com.yolo.keyboard.module.system.service.tenant.TenantService;
|
import com.yolo.keyboard.module.system.service.tenant.TenantService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -113,6 +114,13 @@ public class TenantController {
|
|||||||
return success(BeanUtils.toBean(tenant, TenantRespVO.class));
|
return success(BeanUtils.toBean(tenant, TenantRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/current")
|
||||||
|
@Operation(summary = "获得当前登录租户信息")
|
||||||
|
public CommonResult<TenantInfoRespVO> getCurrentTenant() {
|
||||||
|
TenantDO tenant = tenantService.getCurrentTenant();
|
||||||
|
return success(BeanUtils.toBean(tenant, TenantInfoRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获得租户分页")
|
@Operation(summary = "获得租户分页")
|
||||||
@PreAuthorize("@ss.hasPermission('system:tenant:query')")
|
@PreAuthorize("@ss.hasPermission('system:tenant:query')")
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.yolo.keyboard.module.system.controller.admin.tenant.vo.tenant;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 当前租户信息 Response VO")
|
||||||
|
@Data
|
||||||
|
public class TenantInfoRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "上级租户编号", example = "1")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@Schema(description = "代理级别", example = "1")
|
||||||
|
private Integer tenantLevel;
|
||||||
|
|
||||||
|
@Schema(description = "分润比例", example = "0.3")
|
||||||
|
private BigDecimal profitShareRatio;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
|
|||||||
.likeIfPresent(TenantDO::getContactName, reqVO.getContactName())
|
.likeIfPresent(TenantDO::getContactName, reqVO.getContactName())
|
||||||
.likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile())
|
.likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile())
|
||||||
.eqIfPresent(TenantDO::getStatus, reqVO.getStatus())
|
.eqIfPresent(TenantDO::getStatus, reqVO.getStatus())
|
||||||
|
.eqIfPresent(TenantDO::getParentId, reqVO.getParentId())
|
||||||
.betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime())
|
.betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime())
|
||||||
.orderByDesc(TenantDO::getId));
|
.orderByDesc(TenantDO::getId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,4 +142,11 @@ public interface TenantService {
|
|||||||
*/
|
*/
|
||||||
void validTenant(Long id);
|
void validTenant(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录租户信息
|
||||||
|
*
|
||||||
|
* @return 当前租户
|
||||||
|
*/
|
||||||
|
TenantDO getCurrentTenant();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ public class TenantServiceImpl implements TenantService {
|
|||||||
}
|
}
|
||||||
tenant.setParentId(currentTenantId);
|
tenant.setParentId(currentTenantId);
|
||||||
tenant.setTenantLevel(parentTenant != null && parentTenant.getTenantLevel() != null ? parentTenant.getTenantLevel() + 1 : 1);
|
tenant.setTenantLevel(parentTenant != null && parentTenant.getTenantLevel() != null ? parentTenant.getTenantLevel() + 1 : 1);
|
||||||
|
|
||||||
|
// 如果当前用户是1级代理,下级分成比例沿用自己的分成比例,不允许单独设置
|
||||||
|
if (parentTenant != null && parentTenant.getTenantLevel() != null && parentTenant.getTenantLevel() == 1) {
|
||||||
|
tenant.setProfitShareRatio(parentTenant.getProfitShareRatio());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tenantMapper.insert(tenant);
|
tenantMapper.insert(tenant);
|
||||||
@@ -285,6 +290,15 @@ public class TenantServiceImpl implements TenantService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
|
public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
|
||||||
|
// 如果当前租户是一级代理(tenantLevel=1),只能查询自己的下级租户
|
||||||
|
Long currentTenantId = TenantContextHolder.getTenantId();
|
||||||
|
if (currentTenantId != null) {
|
||||||
|
TenantDO currentTenant = tenantMapper.selectById(currentTenantId);
|
||||||
|
if (currentTenant != null && currentTenant.getTenantLevel() != null
|
||||||
|
&& currentTenant.getTenantLevel() == 1) {
|
||||||
|
pageReqVO.setParentId(currentTenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
return tenantMapper.selectPage(pageReqVO);
|
return tenantMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,4 +366,10 @@ public class TenantServiceImpl implements TenantService {
|
|||||||
return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
|
return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TenantDO getCurrentTenant() {
|
||||||
|
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||||
|
return getTenant(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user