feat: 重构充值提现功能,添加冷钱包管理
后端改动: - 新增冷钱包管理模块(ColdWallet实体、Mapper、Service、Controller) - 充值流程:创建订单→显示钱包地址→用户确认打款→管理员审核 - 提现流程:用户输入地址和联系方式→冻结余额→管理员审核 - OrderFund新增字段:walletId, walletAddress, withdrawContact, payTime, confirmTime 前端改动(monisuo-admin): - 新增冷钱包管理页面(wallets.vue) - 优化订单管理页面,支持新的状态流转 - 添加调试日志帮助排查登录问题 前端改动(flutter_monisuo): - 更新OrderFund模型支持新字段 - 充值成功后显示钱包地址弹窗 - 提现时收集提现地址和联系方式 - 新增资金订单页面 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -264,17 +264,22 @@ public class AdminController {
|
||||
|
||||
/**
|
||||
* 待审批订单
|
||||
* 充值待确认(type=1,status=2) + 提现待审批(type=2,status=1)
|
||||
*/
|
||||
@GetMapping("/order/pending")
|
||||
public Result<Map<String, Object>> getPendingOrders(
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
IPage<OrderFund> page = fundService.getPendingOrders(pageNum, pageSize);
|
||||
IPage<OrderFund> page = fundService.getPendingOrders(type, status, pageNum, pageSize);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("list", page.getRecords());
|
||||
data.put("total", page.getTotal());
|
||||
data.put("pageNum", page.getCurrent());
|
||||
data.put("pageSize", page.getSize());
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.it.rattan.monisuo.controller;
|
||||
|
||||
import com.it.rattan.monisuo.common.Result;
|
||||
import com.it.rattan.monisuo.entity.ColdWallet;
|
||||
import com.it.rattan.monisuo.service.ColdWalletService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 冷钱包地址接口
|
||||
*/
|
||||
@RestController
|
||||
public class ColdWalletController {
|
||||
|
||||
@Autowired
|
||||
private ColdWalletService coldWalletService;
|
||||
|
||||
/**
|
||||
* 管理端 - 获取钱包列表
|
||||
*/
|
||||
@GetMapping("/admin/wallet/list")
|
||||
public Result<List<ColdWallet>> getList() {
|
||||
List<ColdWallet> list = coldWalletService.getList();
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端 - 新增钱包
|
||||
*/
|
||||
@PostMapping("/admin/wallet/create")
|
||||
public Result<Void> create(@RequestBody ColdWallet wallet) {
|
||||
if (wallet.getName() == null || wallet.getName().isEmpty()) {
|
||||
return Result.fail("钱包名称不能为空");
|
||||
}
|
||||
if (wallet.getAddress() == null || wallet.getAddress().isEmpty()) {
|
||||
return Result.fail("钱包地址不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
coldWalletService.create(wallet);
|
||||
return Result.success("创建成功", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端 - 更新钱包
|
||||
*/
|
||||
@PostMapping("/admin/wallet/update")
|
||||
public Result<Void> update(@RequestBody ColdWallet wallet) {
|
||||
if (wallet.getId() == null) {
|
||||
return Result.fail("钱包ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
coldWalletService.update(wallet);
|
||||
return Result.success("更新成功", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端 - 删除钱包
|
||||
*/
|
||||
@PostMapping("/admin/wallet/delete")
|
||||
public Result<Void> delete(@RequestBody Map<String, Long> params) {
|
||||
Long id = params.get("id");
|
||||
if (id == null) {
|
||||
return Result.fail("钱包ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
coldWalletService.delete(id);
|
||||
return Result.success("删除成功", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端 - 设置默认钱包
|
||||
*/
|
||||
@PostMapping("/admin/wallet/setDefault")
|
||||
public Result<Void> setDefault(@RequestBody Map<String, Long> params) {
|
||||
Long id = params.get("id");
|
||||
if (id == null) {
|
||||
return Result.fail("钱包ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
coldWalletService.setDefault(id);
|
||||
return Result.success("设置成功", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端 - 切换状态
|
||||
*/
|
||||
@PostMapping("/admin/wallet/toggleStatus")
|
||||
public Result<Void> toggleStatus(@RequestBody Map<String, Long> params) {
|
||||
Long id = params.get("id");
|
||||
if (id == null) {
|
||||
return Result.fail("钱包ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
coldWalletService.toggleStatus(id);
|
||||
return Result.success("操作成功", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户端 - 获取默认钱包地址
|
||||
*/
|
||||
@GetMapping("/api/wallet/default")
|
||||
public Result<Map<String, Object>> getDefaultWallet() {
|
||||
ColdWallet wallet = coldWalletService.getDefaultWallet();
|
||||
if (wallet == null) {
|
||||
return Result.fail("系统暂未配置充值地址");
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("id", wallet.getId());
|
||||
result.put("name", wallet.getName());
|
||||
result.put("address", wallet.getAddress());
|
||||
result.put("network", wallet.getNetwork());
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,30 @@ public class FundController {
|
||||
|
||||
try {
|
||||
Map<String, Object> result = fundService.deposit(userId, amount, remark);
|
||||
return Result.success("申请成功,等待审批", result);
|
||||
return Result.success("申请成功,请完成打款", result);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户确认已打款
|
||||
*/
|
||||
@PostMapping("/confirmPay")
|
||||
public Result<Void> confirmPay(@RequestBody Map<String, String> params) {
|
||||
Long userId = UserContext.getUserId();
|
||||
if (userId == null) {
|
||||
return Result.unauthorized("请先登录");
|
||||
}
|
||||
|
||||
String orderNo = params.get("orderNo");
|
||||
if (orderNo == null || orderNo.isEmpty()) {
|
||||
return Result.fail("订单号不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
fundService.confirmPay(userId, orderNo);
|
||||
return Result.success("已确认打款,等待审核", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
@@ -56,14 +79,20 @@ public class FundController {
|
||||
}
|
||||
|
||||
BigDecimal amount = new BigDecimal(params.get("amount").toString());
|
||||
String withdrawAddress = (String) params.get("withdrawAddress");
|
||||
String withdrawContact = (String) params.get("withdrawContact");
|
||||
String remark = (String) params.get("remark");
|
||||
|
||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return Result.fail("提现金额必须大于0");
|
||||
}
|
||||
|
||||
if (withdrawAddress == null || withdrawAddress.isEmpty()) {
|
||||
return Result.fail("请填写提现地址");
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, Object> result = fundService.withdraw(userId, amount, remark);
|
||||
Map<String, Object> result = fundService.withdraw(userId, amount, withdrawAddress, withdrawContact, remark);
|
||||
return Result.success("申请成功,等待审批", result);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
|
||||
37
src/main/java/com/it/rattan/monisuo/entity/ColdWallet.java
Normal file
37
src/main/java/com/it/rattan/monisuo/entity/ColdWallet.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.it.rattan.monisuo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 冷钱包地址实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("cold_wallet")
|
||||
public class ColdWallet {
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 钱包名称 */
|
||||
private String name;
|
||||
|
||||
/** 钱包地址 */
|
||||
private String address;
|
||||
|
||||
/** 网络类型: TRC20/ERC20/BEP20等 */
|
||||
private String network;
|
||||
|
||||
/** 是否默认: false-否 true-是 */
|
||||
private Boolean isDefault;
|
||||
|
||||
/** 状态: 0-禁用 1-启用 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -33,9 +33,24 @@ public class OrderFund implements Serializable {
|
||||
/** 金额(USDT) */
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 状态: 1-待审批 2-已完成 3-已驳回 4-已取消 */
|
||||
/** 冷钱包ID(充值) */
|
||||
private Long walletId;
|
||||
|
||||
/** 冷钱包地址(充值)/提现地址 */
|
||||
private String walletAddress;
|
||||
|
||||
/** 提现联系方式 */
|
||||
private String withdrawContact;
|
||||
|
||||
/** 状态: 充值-1待付款2待确认3已完成4已驳回5已取消6充值失败; 提现-1待审批2已完成3已驳回4已取消 */
|
||||
private Integer status;
|
||||
|
||||
/** 用户确认打款时间 */
|
||||
private LocalDateTime payTime;
|
||||
|
||||
/** 管理员确认时间 */
|
||||
private LocalDateTime confirmTime;
|
||||
|
||||
/** 审批管理员ID */
|
||||
private Long approveAdminId;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.it.rattan.monisuo.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.it.rattan.monisuo.entity.ColdWallet;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* 冷钱包地址Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface ColdWalletMapper extends BaseMapper<ColdWallet> {
|
||||
|
||||
/**
|
||||
* 清除所有默认标记
|
||||
*/
|
||||
@Update("UPDATE cold_wallet SET is_default = 0")
|
||||
void clearAllDefault();
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.it.rattan.monisuo.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.it.rattan.monisuo.entity.ColdWallet;
|
||||
import com.it.rattan.monisuo.mapper.ColdWalletMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 冷钱包地址服务
|
||||
*/
|
||||
@Service
|
||||
public class ColdWalletService {
|
||||
|
||||
@Autowired
|
||||
private ColdWalletMapper coldWalletMapper;
|
||||
|
||||
/**
|
||||
* 获取所有钱包列表
|
||||
*/
|
||||
public List<ColdWallet> getList() {
|
||||
LambdaQueryWrapper<ColdWallet> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.orderByDesc(ColdWallet::getIsDefault)
|
||||
.orderByDesc(ColdWallet::getCreateTime);
|
||||
return coldWalletMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启用的钱包列表
|
||||
*/
|
||||
public List<ColdWallet> getEnabledList() {
|
||||
LambdaQueryWrapper<ColdWallet> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(ColdWallet::getStatus, 1)
|
||||
.orderByDesc(ColdWallet::getIsDefault)
|
||||
.orderByDesc(ColdWallet::getCreateTime);
|
||||
return coldWalletMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认钱包
|
||||
*/
|
||||
public ColdWallet getDefaultWallet() {
|
||||
LambdaQueryWrapper<ColdWallet> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(ColdWallet::getStatus, 1)
|
||||
.eq(ColdWallet::getIsDefault, true);
|
||||
return coldWalletMapper.selectOne(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取钱包
|
||||
*/
|
||||
public ColdWallet getById(Long id) {
|
||||
return coldWalletMapper.selectById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增钱包
|
||||
*/
|
||||
@Transactional
|
||||
public void create(ColdWallet wallet) {
|
||||
wallet.setCreateTime(LocalDateTime.now());
|
||||
if (wallet.getStatus() == null) {
|
||||
wallet.setStatus(1);
|
||||
}
|
||||
if (wallet.getIsDefault() == null) {
|
||||
wallet.setIsDefault(false);
|
||||
}
|
||||
|
||||
// 如果设为默认,先清除其他默认
|
||||
if (Boolean.TRUE.equals(wallet.getIsDefault())) {
|
||||
coldWalletMapper.clearAllDefault();
|
||||
}
|
||||
|
||||
coldWalletMapper.insert(wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新钱包
|
||||
*/
|
||||
@Transactional
|
||||
public void update(ColdWallet wallet) {
|
||||
wallet.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 如果设为默认,先清除其他默认
|
||||
if (Boolean.TRUE.equals(wallet.getIsDefault())) {
|
||||
coldWalletMapper.clearAllDefault();
|
||||
}
|
||||
|
||||
coldWalletMapper.updateById(wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除钱包
|
||||
*/
|
||||
public void delete(Long id) {
|
||||
coldWalletMapper.deleteById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认钱包
|
||||
*/
|
||||
@Transactional
|
||||
public void setDefault(Long id) {
|
||||
ColdWallet wallet = coldWalletMapper.selectById(id);
|
||||
if (wallet == null) {
|
||||
throw new RuntimeException("钱包不存在");
|
||||
}
|
||||
if (wallet.getStatus() != 1) {
|
||||
throw new RuntimeException("禁用的钱包不能设为默认");
|
||||
}
|
||||
|
||||
// 清除所有默认
|
||||
coldWalletMapper.clearAllDefault();
|
||||
|
||||
// 设置新的默认
|
||||
wallet.setIsDefault(true);
|
||||
wallet.setUpdateTime(LocalDateTime.now());
|
||||
coldWalletMapper.updateById(wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换状态
|
||||
*/
|
||||
public void toggleStatus(Long id) {
|
||||
ColdWallet wallet = coldWalletMapper.selectById(id);
|
||||
if (wallet == null) {
|
||||
throw new RuntimeException("钱包不存在");
|
||||
}
|
||||
|
||||
// 如果是默认钱包要禁用,需要先取消默认
|
||||
if (Boolean.TRUE.equals(wallet.getIsDefault()) && wallet.getStatus() == 1) {
|
||||
wallet.setIsDefault(false);
|
||||
}
|
||||
|
||||
wallet.setStatus(wallet.getStatus() == 1 ? 0 : 1);
|
||||
wallet.setUpdateTime(LocalDateTime.now());
|
||||
coldWalletMapper.updateById(wallet);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package com.it.rattan.monisuo.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
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.it.rattan.monisuo.entity.AccountFund;
|
||||
import com.it.rattan.monisuo.entity.ColdWallet;
|
||||
import com.it.rattan.monisuo.entity.OrderFund;
|
||||
import com.it.rattan.monisuo.entity.User;
|
||||
import com.it.rattan.monisuo.mapper.AccountFundMapper;
|
||||
import com.it.rattan.monisuo.mapper.OrderFundMapper;
|
||||
import com.it.rattan.monisuo.mapper.UserMapper;
|
||||
import com.it.rattan.monisuo.util.OrderNoUtil;
|
||||
@@ -19,6 +20,10 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* 充提服务
|
||||
*
|
||||
* 状态定义:
|
||||
* 充值: 1=待付款, 2=待确认, 3=已完成, 4=已驳回, 5=已取消
|
||||
* 提现: 1=待审批, 2=已完成, 3=已驳回, 4=已取消
|
||||
*/
|
||||
@Service
|
||||
public class FundService {
|
||||
@@ -26,14 +31,20 @@ public class FundService {
|
||||
@Autowired
|
||||
private OrderFundMapper orderFundMapper;
|
||||
|
||||
@Autowired
|
||||
private AccountFundMapper accountFundMapper;
|
||||
|
||||
@Autowired
|
||||
private AssetService assetService;
|
||||
|
||||
@Autowired
|
||||
private ColdWalletService coldWalletService;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
/**
|
||||
* 申请充值
|
||||
* 申请充值 - 关联默认冷钱包
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, Object> deposit(Long userId, BigDecimal amount, String remark) {
|
||||
@@ -46,13 +57,21 @@ public class FundService {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
// 获取默认冷钱包
|
||||
ColdWallet wallet = coldWalletService.getDefaultWallet();
|
||||
if (wallet == null) {
|
||||
throw new RuntimeException("系统暂未配置充值地址");
|
||||
}
|
||||
|
||||
OrderFund order = new OrderFund();
|
||||
order.setOrderNo(OrderNoUtil.fundOrderNo());
|
||||
order.setUserId(userId);
|
||||
order.setUsername(user.getUsername());
|
||||
order.setType(1); // 充值
|
||||
order.setAmount(amount);
|
||||
order.setStatus(1); // 待审批
|
||||
order.setStatus(1); // 待付款
|
||||
order.setWalletId(wallet.getId());
|
||||
order.setWalletAddress(wallet.getAddress());
|
||||
order.setRemark(remark);
|
||||
order.setCreateTime(LocalDateTime.now());
|
||||
orderFundMapper.insert(order);
|
||||
@@ -61,29 +80,65 @@ public class FundService {
|
||||
result.put("orderNo", order.getOrderNo());
|
||||
result.put("amount", amount);
|
||||
result.put("status", order.getStatus());
|
||||
result.put("walletAddress", wallet.getAddress());
|
||||
result.put("walletNetwork", wallet.getNetwork());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请提现
|
||||
* 用户确认已打款
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, Object> withdraw(Long userId, BigDecimal amount, String remark) {
|
||||
public void confirmPay(Long userId, String orderNo) {
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(OrderFund::getUserId, userId)
|
||||
.eq(OrderFund::getOrderNo, orderNo)
|
||||
.eq(OrderFund::getType, 1) // 仅充值订单
|
||||
.eq(OrderFund::getStatus, 1); // 仅待付款可确认
|
||||
|
||||
OrderFund order = orderFundMapper.selectOne(wrapper);
|
||||
if (order == null) {
|
||||
throw new RuntimeException("订单不存在或状态不可操作");
|
||||
}
|
||||
|
||||
order.setStatus(2); // 待确认
|
||||
order.setPayTime(LocalDateTime.now());
|
||||
order.setUpdateTime(LocalDateTime.now());
|
||||
orderFundMapper.updateById(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请提现 - 冻结余额
|
||||
*/
|
||||
@Transactional
|
||||
public Map<String, Object> withdraw(Long userId, BigDecimal amount, String withdrawAddress,
|
||||
String withdrawContact, String remark) {
|
||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new RuntimeException("提现金额必须大于0");
|
||||
}
|
||||
|
||||
if (withdrawAddress == null || withdrawAddress.isEmpty()) {
|
||||
throw new RuntimeException("请填写提现地址");
|
||||
}
|
||||
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
// 检查余额
|
||||
// 检查并冻结余额
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(userId);
|
||||
if (fund.getBalance().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("资金账户余额不足");
|
||||
}
|
||||
|
||||
// 冻结余额
|
||||
fund.setBalance(fund.getBalance().subtract(amount));
|
||||
fund.setFrozen(fund.getFrozen() != null ? fund.getFrozen().add(amount) : amount);
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
accountFundMapper.updateById(fund);
|
||||
|
||||
// 创建订单
|
||||
OrderFund order = new OrderFund();
|
||||
order.setOrderNo(OrderNoUtil.fundOrderNo());
|
||||
order.setUserId(userId);
|
||||
@@ -91,6 +146,8 @@ public class FundService {
|
||||
order.setType(2); // 提现
|
||||
order.setAmount(amount);
|
||||
order.setStatus(1); // 待审批
|
||||
order.setWalletAddress(withdrawAddress);
|
||||
order.setWithdrawContact(withdrawContact);
|
||||
order.setRemark(remark);
|
||||
order.setCreateTime(LocalDateTime.now());
|
||||
orderFundMapper.insert(order);
|
||||
@@ -103,21 +160,38 @@ public class FundService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
* 取消订单 - 仅充值待付款状态可取消
|
||||
*/
|
||||
@Transactional
|
||||
public void cancel(Long userId, String orderNo) {
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(OrderFund::getUserId, userId)
|
||||
.eq(OrderFund::getOrderNo, orderNo)
|
||||
.eq(OrderFund::getStatus, 1); // 仅待审批可取消
|
||||
.eq(OrderFund::getOrderNo, orderNo);
|
||||
|
||||
OrderFund order = orderFundMapper.selectOne(wrapper);
|
||||
if (order == null) {
|
||||
throw new RuntimeException("订单不存在或状态不可取消");
|
||||
throw new RuntimeException("订单不存在");
|
||||
}
|
||||
|
||||
order.setStatus(4); // 已取消
|
||||
// 充值订单仅待付款可取消
|
||||
if (order.getType() == 1 && order.getStatus() != 1) {
|
||||
throw new RuntimeException("当前状态不可取消");
|
||||
}
|
||||
|
||||
// 提现订单仅待审批可取消,需要解冻余额
|
||||
if (order.getType() == 2) {
|
||||
if (order.getStatus() != 1) {
|
||||
throw new RuntimeException("当前状态不可取消");
|
||||
}
|
||||
// 解冻余额
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(userId);
|
||||
fund.setBalance(fund.getBalance().add(order.getAmount()));
|
||||
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
accountFundMapper.updateById(fund);
|
||||
}
|
||||
|
||||
order.setStatus(5); // 已取消
|
||||
order.setUpdateTime(LocalDateTime.now());
|
||||
orderFundMapper.updateById(order);
|
||||
}
|
||||
@@ -141,11 +215,17 @@ public class FundService {
|
||||
* 获取待审批订单数量
|
||||
*/
|
||||
public int getPendingCount() {
|
||||
return orderFundMapper.countPending();
|
||||
// 充值待确认 + 提现待审批
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.and(w -> w.eq(OrderFund::getType, 1).eq(OrderFund::getStatus, 2))
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 1));
|
||||
return Math.toIntExact(orderFundMapper.selectCount(wrapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员审批
|
||||
* 充值: 仅待确认(status=2)可审批
|
||||
* 提现: 仅待审批(status=1)可审批
|
||||
*/
|
||||
@Transactional
|
||||
public void approve(Long adminId, String adminName, String orderNo, Integer status,
|
||||
@@ -157,44 +237,48 @@ public class FundService {
|
||||
throw new RuntimeException("订单不存在");
|
||||
}
|
||||
|
||||
if (order.getStatus() != 1) {
|
||||
throw new RuntimeException("订单已处理");
|
||||
// 充值审批: 仅待确认可审批
|
||||
if (order.getType() == 1 && order.getStatus() != 2) {
|
||||
throw new RuntimeException("该充值订单不可审批,等待用户确认打款");
|
||||
}
|
||||
|
||||
// 提现审批: 仅待审批可审批
|
||||
if (order.getType() == 2 && order.getStatus() != 1) {
|
||||
throw new RuntimeException("该提现订单已处理");
|
||||
}
|
||||
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(order.getUserId());
|
||||
|
||||
if (status == 2) {
|
||||
// 审批通过
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(order.getUserId());
|
||||
|
||||
if (order.getType() == 1) {
|
||||
// 充值:增加余额
|
||||
// 充值通过:增加余额
|
||||
BigDecimal balanceBefore = fund.getBalance();
|
||||
fund.setBalance(fund.getBalance().add(order.getAmount()));
|
||||
fund.setTotalDeposit(fund.getTotalDeposit().add(order.getAmount()));
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
accountFundMapper.updateById(fund);
|
||||
|
||||
// 记录流水
|
||||
assetService.createFlow(order.getUserId(), 1, order.getAmount(),
|
||||
balanceBefore, fund.getBalance(), "USDT", orderNo, "充值");
|
||||
} else {
|
||||
// 提现:扣减余额
|
||||
if (fund.getBalance().compareTo(order.getAmount()) < 0) {
|
||||
throw new RuntimeException("用户余额不足");
|
||||
// 提现通过:从冻结转为扣除,更新累计提现
|
||||
if (fund.getFrozen().compareTo(order.getAmount()) < 0) {
|
||||
throw new RuntimeException("冻结金额不足");
|
||||
}
|
||||
fund.setBalance(fund.getBalance().subtract(order.getAmount()));
|
||||
BigDecimal balanceBefore = fund.getBalance();
|
||||
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
|
||||
fund.setTotalWithdraw(fund.getTotalWithdraw().add(order.getAmount()));
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
accountFundMapper.updateById(fund);
|
||||
|
||||
// 记录流水 (负数表示支出)
|
||||
assetService.createFlow(order.getUserId(), 2, order.getAmount().negate(),
|
||||
balanceBefore, fund.getBalance(), "USDT", orderNo, "提现");
|
||||
}
|
||||
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 更新账户
|
||||
LambdaUpdateWrapper<AccountFund> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(AccountFund::getUserId, order.getUserId())
|
||||
.set(AccountFund::getBalance, fund.getBalance())
|
||||
.set(AccountFund::getTotalDeposit, fund.getTotalDeposit())
|
||||
.set(AccountFund::getTotalWithdraw, fund.getTotalWithdraw())
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
assetService.updateFundAccount(updateWrapper);
|
||||
|
||||
// 记录流水
|
||||
int flowType = order.getType() == 1 ? 1 : 2;
|
||||
String remark = order.getType() == 1 ? "充值" : "提现";
|
||||
assetService.createFlow(order.getUserId(), flowType, order.getAmount(),
|
||||
fund.getBalance().subtract(order.getAmount()),
|
||||
fund.getBalance(), "USDT", orderNo, remark);
|
||||
order.setConfirmTime(LocalDateTime.now());
|
||||
|
||||
} else if (status == 3) {
|
||||
// 审批驳回
|
||||
@@ -202,6 +286,19 @@ public class FundService {
|
||||
throw new RuntimeException("请填写驳回原因");
|
||||
}
|
||||
order.setRejectReason(rejectReason);
|
||||
|
||||
if (order.getType() == 2) {
|
||||
// 提现驳回:解冻金额退还
|
||||
BigDecimal balanceBefore = fund.getBalance();
|
||||
fund.setBalance(fund.getBalance().add(order.getAmount()));
|
||||
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
|
||||
fund.setUpdateTime(LocalDateTime.now());
|
||||
accountFundMapper.updateById(fund);
|
||||
|
||||
// 记录流水
|
||||
assetService.createFlow(order.getUserId(), 2, order.getAmount(),
|
||||
balanceBefore, fund.getBalance(), "USDT", orderNo, "提现驳回退还");
|
||||
}
|
||||
}
|
||||
|
||||
order.setStatus(status);
|
||||
@@ -215,11 +312,28 @@ public class FundService {
|
||||
|
||||
/**
|
||||
* 获取待审批订单列表
|
||||
* 充值待确认(status=2) + 提现待审批(status=1)
|
||||
*/
|
||||
public IPage<OrderFund> getPendingOrders(int pageNum, int pageSize) {
|
||||
public IPage<OrderFund> getPendingOrders(Integer type, Integer status, int pageNum, int pageSize) {
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(OrderFund::getStatus, 1)
|
||||
.orderByAsc(OrderFund::getCreateTime);
|
||||
|
||||
if (type != null && type > 0) {
|
||||
// 指定类型
|
||||
wrapper.eq(OrderFund::getType, type);
|
||||
if (type == 1) {
|
||||
// 充值:待确认
|
||||
wrapper.eq(OrderFund::getStatus, status != null ? status : 2);
|
||||
} else {
|
||||
// 提现:待审批
|
||||
wrapper.eq(OrderFund::getStatus, status != null ? status : 1);
|
||||
}
|
||||
} else {
|
||||
// 不指定类型:充值待确认 + 提现待审批
|
||||
wrapper.and(w -> w.eq(OrderFund::getType, 1).eq(OrderFund::getStatus, 2))
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 1));
|
||||
}
|
||||
|
||||
wrapper.orderByAsc(OrderFund::getCreateTime);
|
||||
|
||||
Page<OrderFund> page = new Page<>(pageNum, pageSize);
|
||||
return orderFundMapper.selectPage(page, wrapper);
|
||||
|
||||
@@ -7,6 +7,14 @@ spring:
|
||||
password: JPJ8wYicSGC8aRnk
|
||||
url: jdbc:mysql://8.155.172.147:3306/monisuo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
hikari:
|
||||
minimum-idle: 5
|
||||
maximum-pool-size: 20
|
||||
idle-timeout: 30000
|
||||
pool-name: MonisuoHikariCP
|
||||
max-lifetime: 1800000
|
||||
connection-timeout: 30000
|
||||
connection-test-query: SELECT 1
|
||||
|
||||
|
||||
#mybatis-plus
|
||||
@@ -18,16 +26,4 @@ bean-searcher:
|
||||
params:
|
||||
pagination:
|
||||
start: 1
|
||||
ignore-case-key:
|
||||
|
||||
|
||||
# mybatisplus代码生成器配置
|
||||
generator:
|
||||
username: root
|
||||
password: 123admin
|
||||
driver: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/spccloud?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
|
||||
#dbTableList: #数据库的表,可多张(自己设置)
|
||||
#- rt_company
|
||||
prefix:
|
||||
rt
|
||||
ignore-case-key:
|
||||
Reference in New Issue
Block a user