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 54d69f6..485f31d 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 @@ -101,4 +101,28 @@ public class TenantBalanceController { BeanUtils.toBean(list, TenantBalanceRespVO.class)); } + @PostMapping("/addAmount") + @Operation(summary = "添加租户余额") + @PreAuthorize("@ss.hasPermission('system:tenant-balance:add')") + public CommonResult addTenantBalance(@Valid @RequestBody TenantBalanceAddReqVO addReqVO) { + tenantBalanceService.addTenantBalance(addReqVO); + return success(true); + } + + @GetMapping("/get-self-amount") + @Operation(summary = "获得自己的余额") + @PreAuthorize("@ss.hasPermission('system:tenant-balance:self-amount')") + public CommonResult getTenantBalance() { + TenantBalanceDO tenantBalance = tenantBalanceService.getSelfBalance(); + return success(BeanUtils.toBean(tenantBalance, TenantBalanceRespVO.class)); + } + + @GetMapping("/get-self-subordinate-amount-page") + @Operation(summary = "获得自己下级余额的分页") + @PreAuthorize("@ss.hasPermission('system:tenant-balance:self-subordinate')") + public CommonResult> getSelfSubordinate(@Valid TenantBalancePageReqVO pageReqVO) { + PageResult tenantBalancePage = tenantBalanceService.getSelfSubordinateTenantBalancePage(pageReqVO); + return success(BeanUtils.toBean(tenantBalancePage, TenantBalanceRespVO.class)); + } + } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceAddReqVO.java b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceAddReqVO.java new file mode 100644 index 0000000..efb6e40 --- /dev/null +++ b/keyboard-server/src/main/java/com/yolo/keyboard/controller/admin/tenantbalance/vo/TenantBalanceAddReqVO.java @@ -0,0 +1,24 @@ +package com.yolo.keyboard.controller.admin.tenantbalance.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + + +/* + * @author: ziin + * @date: 2025/11/19 21:05 + */ +@Schema(description = "管理后台 - 租户余额添加VO") +@Data +public class TenantBalanceAddReqVO { + @Schema(description = "租户 Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "19954") + private Long id; + + @Schema(description = "增加的余额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000000") + 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 0c9c3ef..f3bb9c9 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 @@ -2,6 +2,8 @@ package com.yolo.keyboard.dal.dataobject.tenantbalance; import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore; import lombok.*; + +import java.math.BigDecimal; import java.util.*; import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.*; @@ -31,10 +33,11 @@ public class TenantBalanceDO extends BaseDO { /** * 当前积分余额 */ - private Integer balance; + private BigDecimal balance; /** * 乐观锁版本号 */ + @Version private Integer version; /** * 更新时间 diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalancetransaction/TenantBalanceTransactionDO.java b/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalancetransaction/TenantBalanceTransactionDO.java new file mode 100644 index 0000000..fc87e58 --- /dev/null +++ b/keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/tenantbalancetransaction/TenantBalanceTransactionDO.java @@ -0,0 +1,70 @@ +package com.yolo.keyboard.dal.dataobject.tenantbalancetransaction; + +import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore; +import lombok.*; + +import java.math.BigDecimal; +import java.util.*; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO; + +/** + * 租户积分记录 DO + * + * @author 芋道源码 + */ +@TableName("system_tenant_balance_transaction") +@KeySequence("system_tenant_balance_transaction_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TenantIgnore +public class TenantBalanceTransactionDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 本次变动点数,正加负减 + */ + private BigDecimal points; + /** + * 变动后余额快照(冗余) + */ + private BigDecimal balance; + /** + * 变动类型,如 TRANSFER_IN, WITHDRAW,REFUND_COMMISSION ,RECHARGE_COMMISSION + */ + private String type; + /** + * 变动描述 + */ + private String description; + /** + * 订单 Id/业务单号 + */ + private String orderId; + /** + * 业务流水号(转账、订单等唯一标识) + */ + private String bizNo; + /** + * 操作人 Id + */ + private Long operatorId; + /** + * 创建时间 + */ + private LocalDateTime createdAt; + /** + * 备注 + */ + private String remark; + + private Long tenantId; +} \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/dal/mysql/tenantbalancetransaction/TenantBalanceTransactionMapper.java b/keyboard-server/src/main/java/com/yolo/keyboard/dal/mysql/tenantbalancetransaction/TenantBalanceTransactionMapper.java new file mode 100644 index 0000000..5e5bc7a --- /dev/null +++ b/keyboard-server/src/main/java/com/yolo/keyboard/dal/mysql/tenantbalancetransaction/TenantBalanceTransactionMapper.java @@ -0,0 +1,14 @@ +package com.yolo.keyboard.dal.mysql.tenantbalancetransaction; + +import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO; +import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 租户积分记录 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface TenantBalanceTransactionMapper extends BaseMapperX { +} 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 89917d3..1611523 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 @@ -59,4 +59,26 @@ public interface TenantBalanceService { */ PageResult getTenantBalancePage(TenantBalancePageReqVO pageReqVO); + /** + * 添加租户余额 + * + * @param addReqVO 添加信息 + */ + void addTenantBalance(@Valid TenantBalanceAddReqVO addReqVO); + + /** + * 获得自己的余额 + * + * @return 租户余额 + */ + TenantBalanceDO getSelfBalance(); + + /** + * 获得自己下级余额的分页 + * + * @param pageReqVO 分页查询 + * @return 租户余额分页 + */ + PageResult getSelfSubordinateTenantBalancePage(TenantBalancePageReqVO pageReqVO); + } \ 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 692e55f..0210612 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,16 @@ package com.yolo.keyboard.service.tenantbalance; import cn.hutool.core.collection.CollUtil; +import com.yolo.keyboard.dal.dataobject.tenantbalancetransaction.TenantBalanceTransactionDO; +import com.yolo.keyboard.dal.mysql.tenantbalancetransaction.TenantBalanceTransactionMapper; +import com.yolo.keyboard.framework.tenant.core.context.TenantContextHolder; +import com.yolo.keyboard.utils.BizNoGenerator; import org.springframework.stereotype.Service; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.util.*; import com.yolo.keyboard.controller.admin.tenantbalance.vo.*; import com.yolo.keyboard.dal.dataobject.tenantbalance.TenantBalanceDO; @@ -32,6 +37,9 @@ public class TenantBalanceServiceImpl implements TenantBalanceService { @Resource private TenantBalanceMapper tenantBalanceMapper; + @Resource + private TenantBalanceTransactionMapper tenantBalanceTransactionMapper; + @Override public Long createTenantBalance(TenantBalanceSaveReqVO createReqVO) { // 插入 @@ -82,4 +90,47 @@ public class TenantBalanceServiceImpl implements TenantBalanceService { return tenantBalanceMapper.selectPage(pageReqVO); } + @Override + @Transactional + public void addTenantBalance(TenantBalanceAddReqVO addReqVO) { + // 1. 根据ID查询租户余额记录,校验是否存在 + TenantBalanceDO balance = tenantBalanceMapper.selectById(addReqVO.getId()); + if (balance == null) { + throw exception(TENANT_BALANCE_NOT_EXISTS); + } + + // 2. 计算新的余额(当前余额 + 充值金额) + BigDecimal newBalance = balance.getBalance().add(new BigDecimal(String.valueOf(addReqVO.getAmount()))); + // 3. 更新租户余额(使用乐观锁) + balance.setBalance(newBalance); + int updateCount = tenantBalanceMapper.updateById(balance); + if (updateCount == 0) { + throw exception(TENANT_BALANCE_NOT_EXISTS); + } + + // 4. 创建余额交易记录 + TenantBalanceTransactionDO transaction = TenantBalanceTransactionDO.builder() + .bizNo(BizNoGenerator.generate("RECHARGE")) // 生成充值业务编号 + .points(new BigDecimal(String.valueOf(addReqVO.getAmount()))) // 充值金额 + .balance(newBalance) // 充值后余额 + .tenantId(addReqVO.getId()) + .type("RECHARGE") // 交易类型:充值 + .description("余额充值") // 交易描述 + .remark(addReqVO.getRemark()) // 备注信息 + .operatorId(TenantContextHolder.getTenantId()) // 操作人ID(当前租户ID) + .build(); + // 5. 插入交易记录到数据库 + tenantBalanceTransactionMapper.insert(transaction); + } + + @Override + public TenantBalanceDO getSelfBalance() { + return null; + } + + @Override + public PageResult getSelfSubordinateTenantBalancePage(TenantBalancePageReqVO pageReqVO) { + return null; + } + } \ No newline at end of file diff --git a/keyboard-server/src/main/java/com/yolo/keyboard/utils/BizNoGenerator.java b/keyboard-server/src/main/java/com/yolo/keyboard/utils/BizNoGenerator.java new file mode 100644 index 0000000..d1d3837 --- /dev/null +++ b/keyboard-server/src/main/java/com/yolo/keyboard/utils/BizNoGenerator.java @@ -0,0 +1,9 @@ +package com.yolo.keyboard.utils; + +import cn.hutool.core.util.IdUtil; + +public class BizNoGenerator { + public static String generate(String prefix) { + return prefix + "-" + IdUtil.getSnowflakeNextIdStr(); + } +} diff --git a/yolo-framework/yolo-spring-boot-starter-mybatis/src/main/java/com/yolo/keyboard/framework/mybatis/config/YoloMybatisAutoConfiguration.java b/yolo-framework/yolo-spring-boot-starter-mybatis/src/main/java/com/yolo/keyboard/framework/mybatis/config/YoloMybatisAutoConfiguration.java index d85f8ba..da93c8d 100644 --- a/yolo-framework/yolo-spring-boot-starter-mybatis/src/main/java/com/yolo/keyboard/framework/mybatis/config/YoloMybatisAutoConfiguration.java +++ b/yolo-framework/yolo-spring-boot-starter-mybatis/src/main/java/com/yolo/keyboard/framework/mybatis/config/YoloMybatisAutoConfiguration.java @@ -14,6 +14,7 @@ import com.baomidou.mybatisplus.extension.incrementer.*; import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.ibatis.annotations.Mapper; @@ -48,6 +49,7 @@ public class YoloMybatisAutoConfiguration { public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件 + mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件 // ↓↓↓ 按需开启,可能会影响到 updateBatch 的地方:例如说文件配置管理 ↓↓↓ // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 拦截没有指定条件的 update 和 delete 语句 return mybatisPlusInterceptor;