111
This commit is contained in:
@@ -17,6 +17,9 @@ public class UserContext {
|
||||
/** 类型: user/admin */
|
||||
private String type;
|
||||
|
||||
/** 角色: 1-超级管理员 2-普通管理员 */
|
||||
private Integer role;
|
||||
|
||||
private static final ThreadLocal<UserContext> CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void set(UserContext context) {
|
||||
@@ -45,4 +48,24 @@ public class UserContext {
|
||||
UserContext context = get();
|
||||
return context != null && "admin".equals(context.type);
|
||||
}
|
||||
|
||||
public static boolean isSuperAdmin() {
|
||||
UserContext context = get();
|
||||
return context != null && "admin".equals(context.type) && context.role != null && context.role == 1;
|
||||
}
|
||||
|
||||
public static Integer getRole() {
|
||||
UserContext context = get();
|
||||
return context != null ? context.role : null;
|
||||
}
|
||||
|
||||
public static boolean isFinance() {
|
||||
UserContext context = get();
|
||||
return context != null && "admin".equals(context.type) && context.role != null && context.role == 3;
|
||||
}
|
||||
|
||||
public static boolean isRegularAdmin() {
|
||||
UserContext context = get();
|
||||
return context != null && "admin".equals(context.type) && context.role != null && context.role == 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.it.rattan.monisuo.common.Result;
|
||||
import com.it.rattan.monisuo.context.UserContext;
|
||||
import com.it.rattan.monisuo.entity.*;
|
||||
import com.it.rattan.monisuo.mapper.AccountFundMapper;
|
||||
import com.it.rattan.monisuo.mapper.OrderFundMapper;
|
||||
@@ -13,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -37,12 +37,17 @@ public class AdminController {
|
||||
@Autowired
|
||||
private AssetService assetService;
|
||||
|
||||
@Autowired
|
||||
private AdminService adminService;
|
||||
|
||||
@Autowired
|
||||
private AccountFundMapper accountFundMapper;
|
||||
|
||||
@Autowired
|
||||
private OrderFundMapper orderFundMapper;
|
||||
|
||||
// ==================== 公开接口 ====================
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
*/
|
||||
@@ -55,32 +60,135 @@ public class AdminController {
|
||||
return Result.fail("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
// 简单验证,实际应从数据库查询
|
||||
// 这里预置账号: admin/admin123, superadmin/admin123
|
||||
String validPassword = "$2a$12$VSVV5o6OXguuFh4f7JvQielF22zxvsvjITkOr/K8D5TnZZcVtQ0B2"; // admin123
|
||||
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder encoder =
|
||||
new org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();
|
||||
|
||||
// 临时处理:预置账号
|
||||
if (("admin".equals(username) || "superadmin".equals(username)) &&
|
||||
encoder.matches(password, validPassword)) {
|
||||
String token = JwtUtil.createToken(1L, username, "admin");
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
Map<String, Object> adminInfo = new HashMap<>();
|
||||
adminInfo.put("id", 1L);
|
||||
adminInfo.put("username", username);
|
||||
adminInfo.put("nickname", "超级管理员");
|
||||
adminInfo.put("role", 1);
|
||||
adminInfo.put("avatar", "/avatars/admin.jpg");
|
||||
result.put("adminInfo", adminInfo);
|
||||
return Result.success("登录成功", result);
|
||||
Admin admin = adminService.login(username, password);
|
||||
if (admin == null) {
|
||||
return Result.fail("用户名或密码错误");
|
||||
}
|
||||
|
||||
return Result.fail("用户名或密码错误");
|
||||
// 生成JWT Token(含角色信息)
|
||||
String token = JwtUtil.createToken(admin.getId(), admin.getUsername(), "admin", admin.getRole());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
|
||||
Map<String, Object> adminInfo = new HashMap<>();
|
||||
adminInfo.put("id", admin.getId());
|
||||
adminInfo.put("username", admin.getUsername());
|
||||
adminInfo.put("nickname", admin.getNickname());
|
||||
adminInfo.put("role", admin.getRole());
|
||||
adminInfo.put("avatar", admin.getAvatar());
|
||||
result.put("adminInfo", adminInfo);
|
||||
|
||||
return Result.success("登录成功", result);
|
||||
}
|
||||
|
||||
// ==================== 所有管理员可访问 ====================
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PostMapping("/change-password")
|
||||
public Result<Void> changePassword(@RequestBody Map<String, String> params) {
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
|
||||
if (oldPassword == null || newPassword == null) {
|
||||
return Result.fail("参数不能为空");
|
||||
}
|
||||
if (newPassword.length() < 4) {
|
||||
return Result.fail("新密码长度不能少于4位");
|
||||
}
|
||||
|
||||
try {
|
||||
boolean success = adminService.updatePassword(UserContext.getUserId(), oldPassword, newPassword);
|
||||
if (!success) {
|
||||
return Result.fail("旧密码不正确");
|
||||
}
|
||||
return Result.success("密码修改成功", null);
|
||||
} catch (RuntimeException e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 待审批订单
|
||||
*/
|
||||
@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(type, status, pageNum, pageSize, UserContext.getRole());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有充提订单
|
||||
*/
|
||||
@GetMapping("/order/list")
|
||||
public Result<Map<String, Object>> getAllOrders(
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
// 普通管理员/财务只能看自己审批过的订单
|
||||
Integer adminRole = UserContext.getRole();
|
||||
Long adminId = UserContext.isSuperAdmin() ? null : UserContext.getUserId();
|
||||
IPage<OrderFund> page = fundService.getAllOrders(type, status, pageNum, pageSize, adminId, adminRole);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批订单
|
||||
*/
|
||||
@PostMapping("/order/approve")
|
||||
public Result<Void> approveOrder(@RequestBody Map<String, Object> params) {
|
||||
String orderNo = (String) params.get("orderNo");
|
||||
Integer status = (Integer) params.get("status");
|
||||
String rejectReason = (String) params.get("rejectReason");
|
||||
String adminRemark = (String) params.get("adminRemark");
|
||||
|
||||
if (orderNo == null || status == null) {
|
||||
return Result.fail("参数错误");
|
||||
}
|
||||
|
||||
if (status != 2 && status != 3) {
|
||||
return Result.fail("状态参数错误");
|
||||
}
|
||||
|
||||
try {
|
||||
Long adminId = UserContext.getUserId();
|
||||
Integer adminRole = UserContext.getRole();
|
||||
// 查询管理员昵称,拼成 "昵称(用户名)" 格式
|
||||
Admin currentAdmin = adminService.getById(adminId);
|
||||
String adminDisplay = currentAdmin != null
|
||||
? currentAdmin.getNickname() + "(" + currentAdmin.getUsername() + ")"
|
||||
: "管理员";
|
||||
fundService.approve(adminId != null ? adminId : 1L, adminDisplay, adminRole,
|
||||
orderNo, status, rejectReason, adminRemark);
|
||||
return Result.success(status == 2 ? "审批通过" : "已驳回", null);
|
||||
} catch (Exception e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 仅超级管理员可访问 ====================
|
||||
|
||||
/**
|
||||
* 用户列表
|
||||
*/
|
||||
@@ -91,6 +199,10 @@ public class AdminController {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
|
||||
if (username != null && !username.isEmpty()) {
|
||||
wrapper.like(User::getUsername, username);
|
||||
@@ -116,6 +228,10 @@ public class AdminController {
|
||||
*/
|
||||
@GetMapping("/user/detail")
|
||||
public Result<Map<String, Object>> getUserDetail(@RequestParam Long userId) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
User user = userService.getById(userId);
|
||||
if (user == null) {
|
||||
return Result.fail("用户不存在");
|
||||
@@ -124,11 +240,9 @@ public class AdminController {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("user", user);
|
||||
|
||||
// 资产信息
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(userId);
|
||||
data.put("fund", fund);
|
||||
|
||||
// 交易账户
|
||||
List<Map<String, Object>> trade = assetService.getTradeAccount(userId);
|
||||
data.put("trade", trade);
|
||||
|
||||
@@ -140,6 +254,10 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/user/status")
|
||||
public Result<Void> updateUserStatus(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
Long userId = Long.valueOf(params.get("userId").toString());
|
||||
Integer status = (Integer) params.get("status");
|
||||
|
||||
@@ -155,11 +273,47 @@ public class AdminController {
|
||||
return Result.success(status == 1 ? "已启用" : "已禁用", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户提升为管理员
|
||||
*/
|
||||
@PostMapping("/user/promote-admin")
|
||||
public Result<Map<String, Object>> promoteUserToAdmin(@RequestBody Map<String, String> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
String username = params.get("username");
|
||||
String password = params.get("password");
|
||||
String nickname = params.get("nickname");
|
||||
|
||||
if (username == null || password == null) {
|
||||
return Result.fail("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
Admin admin = adminService.createAdmin(username, password,
|
||||
nickname != null ? nickname : username, 2);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", admin.getId());
|
||||
data.put("username", admin.getUsername());
|
||||
data.put("nickname", admin.getNickname());
|
||||
data.put("role", admin.getRole());
|
||||
return Result.success("管理员创建成功", data);
|
||||
} catch (RuntimeException e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 币种列表
|
||||
*/
|
||||
@GetMapping("/coin/list")
|
||||
public Result<Map<String, Object>> getCoinList() {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
List<Coin> coins = coinService.list();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("list", coins);
|
||||
@@ -171,6 +325,10 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/coin/save")
|
||||
public Result<Void> saveCoin(@RequestBody Coin coin) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
if (coin.getCode() == null || coin.getCode().isEmpty()) {
|
||||
return Result.fail("币种代码不能为空");
|
||||
}
|
||||
@@ -181,11 +339,17 @@ public class AdminController {
|
||||
coin.setCode(coin.getCode().toUpperCase());
|
||||
|
||||
if (coin.getId() == null) {
|
||||
// 新增
|
||||
coin.setCreateTime(LocalDateTime.now());
|
||||
coinService.save(coin);
|
||||
} else {
|
||||
// 编辑
|
||||
Coin existing = coinService.getById(coin.getId());
|
||||
if (existing == null) {
|
||||
return Result.fail("币种不存在");
|
||||
}
|
||||
coin.setCode(existing.getCode());
|
||||
if (existing.getInitialPrice() != null) {
|
||||
coin.setInitialPrice(existing.getInitialPrice());
|
||||
}
|
||||
coin.setUpdateTime(LocalDateTime.now());
|
||||
coinService.updateById(coin);
|
||||
}
|
||||
@@ -198,12 +362,15 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/coin/price")
|
||||
public Result<Void> updateCoinPrice(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
String code = (String) params.get("code");
|
||||
BigDecimal price = new BigDecimal(params.get("price").toString());
|
||||
|
||||
// USDT价格固定为1,不允许修改
|
||||
if ("USDT".equalsIgnoreCase(code)) {
|
||||
return Result.fail("USDT价格固定为1,不可修改");
|
||||
if ("BTC".equalsIgnoreCase(code) || "ETH".equalsIgnoreCase(code) || "USDT".equalsIgnoreCase(code)) {
|
||||
return Result.fail(code.toUpperCase() + " 价格不可调整");
|
||||
}
|
||||
|
||||
Coin coin = coinService.getCoinByCode(code);
|
||||
@@ -215,9 +382,21 @@ public class AdminController {
|
||||
return Result.fail("实时币种价格不可手动修改");
|
||||
}
|
||||
|
||||
if (price.compareTo(coin.getPrice()) < 0) {
|
||||
return Result.fail("价格不允许下调,当前价格: " + coin.getPrice());
|
||||
}
|
||||
if (price.compareTo(coin.getPrice()) == 0) {
|
||||
return Result.fail("新价格与当前价格相同");
|
||||
}
|
||||
|
||||
if (coin.getInitialPrice() == null) {
|
||||
coin.setInitialPrice(coin.getPrice());
|
||||
}
|
||||
|
||||
coin.setPrice(price);
|
||||
coin.setUpdateTime(LocalDateTime.now());
|
||||
coinService.updateById(coin);
|
||||
coinService.clearCache(code);
|
||||
|
||||
return Result.success("价格已更新", null);
|
||||
}
|
||||
@@ -227,6 +406,10 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/coin/status")
|
||||
public Result<Void> updateCoinStatus(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
Long coinId = Long.valueOf(params.get("coinId").toString());
|
||||
Integer status = (Integer) params.get("status");
|
||||
|
||||
@@ -247,16 +430,16 @@ public class AdminController {
|
||||
*/
|
||||
@PostMapping("/coin/calculator")
|
||||
public Result<Map<String, Object>> calculatePrice(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
BigDecimal currentPrice = new BigDecimal(params.get("currentPrice").toString());
|
||||
BigDecimal holdingAmount = new BigDecimal(params.getOrDefault("holdingAmount", "1000").toString());
|
||||
BigDecimal targetProfit = new BigDecimal(params.get("targetProfit").toString());
|
||||
|
||||
// 计算目标价格
|
||||
// 持仓数量 = 持仓金额 / 当前价格
|
||||
BigDecimal quantity = holdingAmount.divide(currentPrice, 8, BigDecimal.ROUND_DOWN);
|
||||
// 单币盈亏 = 目标盈亏 / 持仓数量
|
||||
BigDecimal profitPerCoin = targetProfit.divide(quantity, 8, BigDecimal.ROUND_DOWN);
|
||||
// 目标价格 = 当前价格 + 单币盈亏
|
||||
BigDecimal targetPrice = currentPrice.add(profitPerCoin);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
@@ -267,131 +450,101 @@ public class AdminController {
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 待审批订单
|
||||
* 充值待确认(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(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有充提订单
|
||||
*/
|
||||
@GetMapping("/order/list")
|
||||
public Result<Map<String, Object>> getAllOrders(
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
IPage<OrderFund> page = fundService.getAllOrders(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批订单
|
||||
*
|
||||
* @param params 参数说明:
|
||||
* - orderNo: 订单号
|
||||
* - status: 审批结果 (2=通过, 3=驳回)
|
||||
* - rejectReason: 驳回原因 (驳回时必填)
|
||||
* - adminRemark: 管理员备注 (可选)
|
||||
*
|
||||
* 注意: 最终订单状态根据订单类型确定
|
||||
* - 充值订单: 审批通过->状态3(已完成), 审批驳回->状态4(已驳回)
|
||||
* - 提现订单: 审批通过->状态2(已完成), 审批驳回->状态3(已驳回)
|
||||
*/
|
||||
@PostMapping("/order/approve")
|
||||
public Result<Void> approveOrder(@RequestBody Map<String, Object> params) {
|
||||
System.out.println("\n==================== 审批订单开始 ====================");
|
||||
System.out.println("[AdminController] 接收到的完整参数: " + params);
|
||||
|
||||
String orderNo = (String) params.get("orderNo");
|
||||
Integer status = (Integer) params.get("status");
|
||||
String rejectReason = (String) params.get("rejectReason");
|
||||
String adminRemark = (String) params.get("adminRemark");
|
||||
|
||||
System.out.println("[AdminController] 解析后的参数:");
|
||||
System.out.println(" - orderNo: " + orderNo);
|
||||
System.out.println(" - status: " + status);
|
||||
System.out.println(" - rejectReason: " + rejectReason);
|
||||
System.out.println(" - adminRemark: " + adminRemark);
|
||||
|
||||
if (orderNo == null || status == null) {
|
||||
System.err.println("[AdminController] 参数校验失败: orderNo或status为空");
|
||||
return Result.fail("参数错误");
|
||||
}
|
||||
|
||||
if (status != 2 && status != 3) {
|
||||
System.err.println("[AdminController] 状态参数错误: " + status);
|
||||
return Result.fail("状态参数错误");
|
||||
}
|
||||
|
||||
try {
|
||||
System.out.println("[AdminController] 开始调用 fundService.approve()...");
|
||||
fundService.approve(1L, "管理员", orderNo, status, rejectReason, adminRemark);
|
||||
System.out.println("[AdminController] fundService.approve() 调用成功");
|
||||
System.out.println("==================== 审批订单结束 ====================\n");
|
||||
return Result.success(status == 2 ? "审批通过" : "已驳回", null);
|
||||
} catch (Exception e) {
|
||||
System.err.println("[AdminController] 审批异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.out.println("==================== 审批订单异常结束 ====================\n");
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 资金总览
|
||||
*/
|
||||
@GetMapping("/finance/overview")
|
||||
public Result<Map<String, Object>> getFinanceOverview() {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
// 累计充值
|
||||
BigDecimal totalDeposit = orderFundMapper.sumCompletedDeposit();
|
||||
data.put("totalDeposit", totalDeposit);
|
||||
// 合并为一次查询获取充值总额、提现总额、待审批数
|
||||
Map<String, Object> fundStats = orderFundMapper.sumFinanceOverview();
|
||||
data.put("totalDeposit", fundStats.get("totalDeposit"));
|
||||
data.put("totalWithdraw", fundStats.get("totalWithdraw"));
|
||||
data.put("pendingCount", ((Number) fundStats.get("pendingCount")).intValue());
|
||||
|
||||
// 累计提现
|
||||
BigDecimal totalWithdraw = orderFundMapper.sumCompletedWithdraw();
|
||||
data.put("totalWithdraw", totalWithdraw);
|
||||
|
||||
// 在管资金
|
||||
BigDecimal fundBalance = accountFundMapper.sumAllBalance();
|
||||
data.put("fundBalance", fundBalance);
|
||||
|
||||
// 交易账户总值
|
||||
BigDecimal tradeValue = accountFundMapper.sumAllTradeValue();
|
||||
data.put("tradeValue", tradeValue != null ? tradeValue : BigDecimal.ZERO);
|
||||
|
||||
// 待审批数量
|
||||
int pendingCount = orderFundMapper.countPending();
|
||||
data.put("pendingCount", pendingCount);
|
||||
|
||||
// 用户总数
|
||||
long userCount = userService.count();
|
||||
data.put("userCount", userCount);
|
||||
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员列表
|
||||
*/
|
||||
@GetMapping("/admin/list")
|
||||
public Result<List<Admin>> getAdminList() {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
List<Admin> admins = adminService.listAdmins();
|
||||
// 清除密码字段
|
||||
admins.forEach(a -> a.setPassword(null));
|
||||
return Result.success(admins);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建管理员/财务
|
||||
*/
|
||||
@PostMapping("/admin/create")
|
||||
public Result<Map<String, Object>> createAdmin(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
String username = (String) params.get("username");
|
||||
String password = (String) params.get("password");
|
||||
String nickname = (String) params.get("nickname");
|
||||
Integer role = params.get("role") != null ? Integer.valueOf(params.get("role").toString()) : 2;
|
||||
|
||||
if (username == null || password == null) {
|
||||
return Result.fail("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
if (role != 2 && role != 3) {
|
||||
return Result.fail("角色参数错误,仅支持管理员(2)或财务(3)");
|
||||
}
|
||||
|
||||
try {
|
||||
Admin admin = adminService.createAdmin(username, password,
|
||||
nickname != null ? nickname : username, role);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", admin.getId());
|
||||
data.put("username", admin.getUsername());
|
||||
data.put("nickname", admin.getNickname());
|
||||
data.put("role", admin.getRole());
|
||||
return Result.success("创建成功", data);
|
||||
} catch (RuntimeException e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用管理员
|
||||
*/
|
||||
@PostMapping("/admin/status")
|
||||
public Result<Void> toggleAdminStatus(@RequestBody Map<String, Object> params) {
|
||||
if (!UserContext.isSuperAdmin()) {
|
||||
return Result.fail("无权限访问");
|
||||
}
|
||||
|
||||
Long adminId = Long.valueOf(params.get("id").toString());
|
||||
Integer status = (Integer) params.get("status");
|
||||
try {
|
||||
adminService.toggleAdminStatus(adminId, status);
|
||||
return Result.success(status == 1 ? "已启用" : "已禁用", null);
|
||||
} catch (RuntimeException e) {
|
||||
return Result.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class Admin implements Serializable {
|
||||
/** 头像URL */
|
||||
private String avatar;
|
||||
|
||||
/** 角色: 1-超级管理员 2-普通管理员 */
|
||||
/** 角色: 1-超级管理员 2-普通管理员 3-财务 */
|
||||
private Integer role;
|
||||
|
||||
/** 权限列表(JSON格式) */
|
||||
|
||||
@@ -32,6 +32,9 @@ public class Coin implements Serializable {
|
||||
/** 当前价格(USDT) */
|
||||
private BigDecimal price;
|
||||
|
||||
/** 初始价格(首次调价时锁定) */
|
||||
private BigDecimal initialPrice;
|
||||
|
||||
/** 美元价格 */
|
||||
private BigDecimal priceUsd;
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ public class OrderFund implements Serializable {
|
||||
/** 金额(USDT) */
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 手续费 */
|
||||
private BigDecimal fee;
|
||||
|
||||
/** 应收款项 */
|
||||
private BigDecimal receivableAmount;
|
||||
|
||||
/** 冷钱包ID(充值) */
|
||||
private Long walletId;
|
||||
|
||||
@@ -44,7 +50,7 @@ public class OrderFund implements Serializable {
|
||||
/** 提现联系方式 */
|
||||
private String withdrawContact;
|
||||
|
||||
/** 状态: 充值-1待付款2待确认3已完成4已驳回5已取消6充值失败; 提现-1待审批2已完成3已驳回4已取消 */
|
||||
/** 状态: 充值-1待付款2待确认3已完成4已驳回5已取消6充值失败; 提现-1待审批2已完成3已驳回4已取消5待财务审核 */
|
||||
private Integer status;
|
||||
|
||||
/** 用户确认打款时间 */
|
||||
@@ -62,6 +68,15 @@ public class OrderFund implements Serializable {
|
||||
/** 审批时间 */
|
||||
private LocalDateTime approveTime;
|
||||
|
||||
/** 财务审批管理员ID */
|
||||
private Long financeAdminId;
|
||||
|
||||
/** 财务审批管理员名称 */
|
||||
private String financeAdminName;
|
||||
|
||||
/** 财务审批时间 */
|
||||
private LocalDateTime financeApproveTime;
|
||||
|
||||
/** 驳回原因 */
|
||||
private String rejectReason;
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ public class TokenFilter implements Filter {
|
||||
context.setUserId(JwtUtil.getUserId(token));
|
||||
context.setUsername(JwtUtil.getUsername(token));
|
||||
context.setType(JwtUtil.getType(token));
|
||||
context.setRole(JwtUtil.getRole(token));
|
||||
UserContext.set(context);
|
||||
|
||||
try {
|
||||
|
||||
@@ -25,6 +25,16 @@ public interface OrderFundMapper extends BaseMapper<OrderFund> {
|
||||
@Select("SELECT COUNT(*) FROM order_fund WHERE status = 1")
|
||||
int countPending();
|
||||
|
||||
/**
|
||||
* 一次性聚合查询:充值总额、提现总额、待审批数
|
||||
*/
|
||||
@Select("SELECT " +
|
||||
"IFNULL(SUM(CASE WHEN type = 1 AND status = 2 THEN amount ELSE 0 END), 0) as totalDeposit, " +
|
||||
"IFNULL(SUM(CASE WHEN type = 2 AND status = 2 THEN amount ELSE 0 END), 0) as totalWithdraw, " +
|
||||
"SUM(CASE WHEN status IN (1, 5) THEN 1 ELSE 0 END) as pendingCount " +
|
||||
"FROM order_fund")
|
||||
Map<String, Object> sumFinanceOverview();
|
||||
|
||||
// ========== 分析相关查询 ==========
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@@ -141,12 +142,26 @@ public class FundService {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
// 检查是否有活跃的提现订单(待审批status=1 或 待财务审核status=5)
|
||||
long activeCount = orderFundMapper.selectCount(
|
||||
new LambdaQueryWrapper<OrderFund>()
|
||||
.eq(OrderFund::getUserId, userId)
|
||||
.eq(OrderFund::getType, 2)
|
||||
.in(OrderFund::getStatus, 1, 5));
|
||||
if (activeCount > 0) {
|
||||
throw new RuntimeException("您有正在处理中的提现订单,请等待处理完成");
|
||||
}
|
||||
|
||||
// 检查并冻结余额
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(userId);
|
||||
if (fund.getBalance().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("资金账户余额不足");
|
||||
}
|
||||
|
||||
// 计算手续费和应收款项
|
||||
BigDecimal fee = amount.multiply(new BigDecimal("0.1")).setScale(8, RoundingMode.DOWN);
|
||||
BigDecimal receivableAmount = amount.subtract(fee);
|
||||
|
||||
// 冻结余额 - 使用 LambdaUpdateWrapper 显式更新
|
||||
BigDecimal newBalance = fund.getBalance().subtract(amount);
|
||||
BigDecimal newFrozen = fund.getFrozen() != null ? fund.getFrozen().add(amount) : amount;
|
||||
@@ -181,6 +196,8 @@ public class FundService {
|
||||
order.setUsername(user.getUsername());
|
||||
order.setType(2); // 提现
|
||||
order.setAmount(amount);
|
||||
order.setFee(fee);
|
||||
order.setReceivableAmount(receivableAmount);
|
||||
order.setStatus(1); // 待审批
|
||||
order.setWalletAddress(withdrawAddress);
|
||||
order.setWithdrawContact(withdrawContact);
|
||||
@@ -191,6 +208,8 @@ public class FundService {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("orderNo", order.getOrderNo());
|
||||
result.put("amount", amount);
|
||||
result.put("fee", fee);
|
||||
result.put("receivableAmount", receivableAmount);
|
||||
result.put("status", order.getStatus());
|
||||
return result;
|
||||
}
|
||||
@@ -267,303 +286,234 @@ public class FundService {
|
||||
* 获取待审批订单数量
|
||||
*/
|
||||
public int getPendingCount() {
|
||||
// 充值待确认 + 提现待审批
|
||||
// 充值待确认 + 提现待审批 + 提现待财务审核
|
||||
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));
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 1))
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 5));
|
||||
return Math.toIntExact(orderFundMapper.selectCount(wrapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员审批
|
||||
* 管理员审批(支持两步审批)
|
||||
* 充值: 仅待确认(status=2)可审批
|
||||
* 提现: 仅待审批(status=1)可审批
|
||||
* 提现第一步(管理员role=2): 仅待审批(status=1)可审批
|
||||
* 提现第二步(财务role=3): 仅待财务审核(status=5)可审批
|
||||
*
|
||||
* @param adminRole 审批人角色: 1-超管 2-管理员 3-财务
|
||||
* @param status 审批结果: 2=通过, 3=驳回
|
||||
*/
|
||||
@Transactional
|
||||
public void approve(Long adminId, String adminName, String orderNo, Integer status,
|
||||
public void approve(Long adminId, String adminName, Integer adminRole, String orderNo, Integer status,
|
||||
String rejectReason, String adminRemark) {
|
||||
System.out.println("\n[FundService.approve] 开始处理审批");
|
||||
System.out.println("[FundService.approve] 参数: orderNo=" + orderNo + ", status=" + status +
|
||||
", adminId=" + adminId + ", adminName=" + adminName);
|
||||
|
||||
// 查询订单
|
||||
System.out.println("[FundService.approve] 步骤1: 查询订单...");
|
||||
OrderFund order = orderFundMapper.selectOne(
|
||||
new LambdaQueryWrapper<OrderFund>().eq(OrderFund::getOrderNo, orderNo));
|
||||
|
||||
if (order == null) {
|
||||
System.err.println("[FundService.approve] 订单不存在: " + orderNo);
|
||||
throw new RuntimeException("订单不存在");
|
||||
}
|
||||
|
||||
System.out.println("[FundService.approve] 订单查询成功:");
|
||||
System.out.println(" - 订单ID: " + order.getId());
|
||||
System.out.println(" - 订单号: " + order.getOrderNo());
|
||||
System.out.println(" - 用户ID: " + order.getUserId());
|
||||
System.out.println(" - 用户名: " + order.getUsername());
|
||||
System.out.println(" - 订单类型: " + (order.getType() == 1 ? "充值" : "提现"));
|
||||
System.out.println(" - 当前状态: " + order.getStatus());
|
||||
System.out.println(" - 金额: " + order.getAmount());
|
||||
|
||||
// 充值审批: 仅待确认可审批
|
||||
if (order.getType() == 1 && order.getStatus() != 2) {
|
||||
System.err.println("[FundService.approve] 充值订单状态不正确,当前状态: " + order.getStatus() + " (需要状态2)");
|
||||
throw new RuntimeException("该充值订单不可审批,等待用户确认打款");
|
||||
// 状态前置检查
|
||||
if (order.getType() == 1) {
|
||||
// 充值:仅待确认可审批
|
||||
if (order.getStatus() != 2) {
|
||||
throw new RuntimeException("该充值订单不可审批,等待用户确认打款");
|
||||
}
|
||||
} else {
|
||||
// 提现:根据审批人角色检查状态
|
||||
if (adminRole == 2 && order.getStatus() != 1) {
|
||||
throw new RuntimeException("该提现订单已处理");
|
||||
}
|
||||
if (adminRole == 3 && order.getStatus() != 5) {
|
||||
throw new RuntimeException("该提现订单不在待财务审核状态");
|
||||
}
|
||||
if (adminRole == 1) {
|
||||
if (order.getStatus() != 1 && order.getStatus() != 5) {
|
||||
throw new RuntimeException("该提现订单已处理");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提现审批: 仅待审批可审批
|
||||
if (order.getType() == 2 && order.getStatus() != 1) {
|
||||
System.err.println("[FundService.approve] 提现订单状态不正确,当前状态: " + order.getStatus() + " (需要状态1)");
|
||||
throw new RuntimeException("该提现订单已处理");
|
||||
}
|
||||
|
||||
// 查询资金账户
|
||||
System.out.println("[FundService.approve] 步骤2: 查询资金账户...");
|
||||
AccountFund fund = assetService.getOrCreateFundAccount(order.getUserId());
|
||||
System.out.println("[FundService.approve] 资金账户查询成功:");
|
||||
System.out.println(" - 账户ID: " + fund.getId());
|
||||
System.out.println(" - 用户ID: " + fund.getUserId());
|
||||
System.out.println(" - 余额: " + fund.getBalance());
|
||||
System.out.println(" - 冻结: " + fund.getFrozen());
|
||||
|
||||
// 根据订单类型和审批结果,确定最终的订单状态
|
||||
// 确定最终状态
|
||||
Integer finalStatus;
|
||||
if (order.getType() == 1) {
|
||||
// 充值订单: 2=通过->3(已完成), 3=驳回->4(已驳回)
|
||||
// 充值: 通过->3(已完成), 驳回->4(已驳回)
|
||||
finalStatus = (status == 2) ? 3 : 4;
|
||||
} else {
|
||||
// 提现订单: 2=通过->2(已完成), 3=驳回->3(已驳回)
|
||||
finalStatus = status;
|
||||
// 提现:
|
||||
// role=2 管理员通过->5(待财务审核), 驳回->3(已驳回)
|
||||
// role=3 财务通过->2(已完成), 驳回->3(已驳回)
|
||||
// role=1 超管通过status=1的订单->5, 通过status=5的订单->2
|
||||
if (status == 3) {
|
||||
finalStatus = 3; // 驳回
|
||||
} else if (adminRole == 2 || (adminRole == 1 && order.getStatus() == 1)) {
|
||||
finalStatus = 5; // 管理员通过/超管审批第一步 -> 待财务审核
|
||||
} else {
|
||||
finalStatus = 2; // 财务通过/超管审批第二步 -> 已完成
|
||||
}
|
||||
}
|
||||
|
||||
// 查询资金账户(提现相关操作需要)
|
||||
AccountFund fund = null;
|
||||
if (order.getType() == 2) {
|
||||
fund = assetService.getOrCreateFundAccount(order.getUserId());
|
||||
}
|
||||
System.out.println("[FundService.approve] 步骤3: 确定最终状态: " + finalStatus +
|
||||
" (审批" + (status == 2 ? "通过" : "驳回") + ")");
|
||||
|
||||
if (status == 2) {
|
||||
// 审批通过
|
||||
System.out.println("[FundService.approve] 步骤4: 处理审批通过逻辑...");
|
||||
|
||||
if (order.getType() == 1) {
|
||||
// 充值通过:增加余额 - 使用 LambdaUpdateWrapper 显式更新
|
||||
System.out.println("[FundService.approve] 处理充值通过...");
|
||||
|
||||
// 处理可能的 null 值
|
||||
BigDecimal currentBalance = fund.getBalance();
|
||||
BigDecimal currentTotalDeposit = fund.getTotalDeposit();
|
||||
if (currentBalance == null) {
|
||||
currentBalance = BigDecimal.ZERO;
|
||||
System.out.println(" - 警告: 账户余额为 null,已设置为 0");
|
||||
}
|
||||
if (currentTotalDeposit == null) {
|
||||
currentTotalDeposit = BigDecimal.ZERO;
|
||||
System.out.println(" - 警告: 累计充值为 null,已设置为 0");
|
||||
}
|
||||
|
||||
BigDecimal balanceBefore = currentBalance;
|
||||
BigDecimal totalDepositBefore = currentTotalDeposit;
|
||||
System.out.println(" - 审批前余额: " + balanceBefore);
|
||||
System.out.println(" - 审批前累计充值: " + totalDepositBefore);
|
||||
System.out.println(" - 账户ID: " + fund.getId());
|
||||
System.out.println(" - 用户ID: " + fund.getUserId());
|
||||
// === 充值通过:增加余额 ===
|
||||
fund = assetService.getOrCreateFundAccount(order.getUserId());
|
||||
BigDecimal currentBalance = fund.getBalance() != null ? fund.getBalance() : BigDecimal.ZERO;
|
||||
BigDecimal currentTotalDeposit = fund.getTotalDeposit() != null ? fund.getTotalDeposit() : BigDecimal.ZERO;
|
||||
|
||||
BigDecimal newBalance = currentBalance.add(order.getAmount());
|
||||
BigDecimal newTotalDeposit = currentTotalDeposit.add(order.getAmount());
|
||||
LocalDateTime updateTime = LocalDateTime.now();
|
||||
|
||||
System.out.println(" - 准备更新账户余额: " + newBalance);
|
||||
System.out.println(" - 准备更新累计充值: " + newTotalDeposit);
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newBalance)
|
||||
.set(AccountFund::getTotalDeposit, newTotalDeposit)
|
||||
.set(AccountFund::getUpdateTime, updateTime);
|
||||
|
||||
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
|
||||
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
|
||||
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
|
||||
|
||||
if (updateResult <= 0) {
|
||||
System.err.println("[FundService.approve] 充值审批更新账户余额失败!");
|
||||
throw new RuntimeException("充值审批更新账户余额失败");
|
||||
}
|
||||
|
||||
// 更新本地对象状态(直接信任 update 返回值,避免额外的 selectById 查询)
|
||||
fund.setBalance(newBalance);
|
||||
fund.setTotalDeposit(newTotalDeposit);
|
||||
fund.setUpdateTime(updateTime);
|
||||
|
||||
// 记录流水
|
||||
assetService.createFlow(order.getUserId(), 1, order.getAmount(),
|
||||
balanceBefore, newBalance, "USDT", orderNo, "充值");
|
||||
currentBalance, newBalance, "USDT", orderNo, "充值");
|
||||
|
||||
System.out.println("[充值审批成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
|
||||
", 充值金额: " + order.getAmount() + " USDT");
|
||||
} else {
|
||||
// 提现通过:从冻结转为扣除,更新累计提现 - 使用 LambdaUpdateWrapper 显式更新
|
||||
System.out.println("[FundService.approve] 处理提现通过...");
|
||||
} else if (finalStatus == 5) {
|
||||
// === 提现管理员通过(第一步):不解冻不扣款,只更新状态 ===
|
||||
// 不需要资金操作
|
||||
|
||||
} else if (finalStatus == 2) {
|
||||
// === 提现财务通过(第二步):从冻结中扣除 ===
|
||||
if (fund.getFrozen().compareTo(order.getAmount()) < 0) {
|
||||
System.err.println("[FundService.approve] 冻结金额不足: 冻结=" + fund.getFrozen() +
|
||||
", 需要=" + order.getAmount());
|
||||
throw new RuntimeException("冻结金额不足");
|
||||
}
|
||||
|
||||
BigDecimal balanceBefore = fund.getBalance();
|
||||
BigDecimal frozenBefore = fund.getFrozen();
|
||||
BigDecimal totalWithdrawBefore = fund.getTotalWithdraw();
|
||||
System.out.println(" - 审批前余额: " + balanceBefore);
|
||||
System.out.println(" - 审批前冻结: " + frozenBefore);
|
||||
System.out.println(" - 审批前累计提现: " + totalWithdrawBefore);
|
||||
|
||||
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
|
||||
BigDecimal newTotalWithdraw = fund.getTotalWithdraw().add(order.getAmount());
|
||||
LocalDateTime updateTime = LocalDateTime.now();
|
||||
|
||||
System.out.println(" - 准备更新账户冻结: " + newFrozen);
|
||||
System.out.println(" - 准备更新累计提现: " + newTotalWithdraw);
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getFrozen, newFrozen)
|
||||
.set(AccountFund::getTotalWithdraw, newTotalWithdraw)
|
||||
.set(AccountFund::getUpdateTime, updateTime);
|
||||
|
||||
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
|
||||
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
|
||||
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
|
||||
|
||||
if (updateResult <= 0) {
|
||||
System.err.println("[FundService.approve] 提现审批更新账户冻结失败!");
|
||||
throw new RuntimeException("提现审批更新账户冻结失败");
|
||||
}
|
||||
|
||||
// 更新本地对象状态
|
||||
fund.setFrozen(newFrozen);
|
||||
fund.setTotalWithdraw(newTotalWithdraw);
|
||||
fund.setUpdateTime(updateTime);
|
||||
|
||||
// 记录流水 (负数表示支出)
|
||||
// 记录流水(负数表示支出)
|
||||
assetService.createFlow(order.getUserId(), 2, order.getAmount().negate(),
|
||||
balanceBefore, balanceBefore, "USDT", orderNo, "提现");
|
||||
|
||||
System.out.println("[提现审批成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
|
||||
", 提现金额: " + order.getAmount() + " USDT");
|
||||
}
|
||||
|
||||
order.setConfirmTime(LocalDateTime.now());
|
||||
|
||||
} else if (status == 3) {
|
||||
// 审批驳回
|
||||
System.out.println("[FundService.approve] 步骤4: 处理审批驳回逻辑...");
|
||||
|
||||
if (rejectReason == null || rejectReason.isEmpty()) {
|
||||
System.err.println("[FundService.approve] 驳回原因为空");
|
||||
throw new RuntimeException("请填写驳回原因");
|
||||
}
|
||||
order.setRejectReason(rejectReason);
|
||||
System.out.println("[FundService.approve] 驳回原因: " + rejectReason);
|
||||
|
||||
if (order.getType() == 2) {
|
||||
// 提现驳回:解冻金额退还 - 使用 LambdaUpdateWrapper 显式更新
|
||||
System.out.println("[FundService.approve] 处理提现驳回,解冻金额退还...");
|
||||
// 提现驳回:解冻金额退还
|
||||
BigDecimal balanceBefore = fund.getBalance();
|
||||
BigDecimal frozenBefore = fund.getFrozen();
|
||||
System.out.println(" - 驳回前余额: " + balanceBefore);
|
||||
System.out.println(" - 驳回前冻结: " + frozenBefore);
|
||||
|
||||
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
|
||||
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
|
||||
LocalDateTime updateTime = LocalDateTime.now();
|
||||
|
||||
System.out.println(" - 准备更新账户: 新余额=" + newBalance + ", 新冻结=" + newFrozen);
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newBalance)
|
||||
.set(AccountFund::getFrozen, newFrozen)
|
||||
.set(AccountFund::getUpdateTime, updateTime);
|
||||
|
||||
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
|
||||
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
|
||||
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
|
||||
|
||||
if (updateResult <= 0) {
|
||||
System.err.println("[FundService.approve] 提现驳回更新账户失败!");
|
||||
throw new RuntimeException("提现驳回更新账户失败");
|
||||
}
|
||||
|
||||
// 更新本地对象状态
|
||||
fund.setBalance(newBalance);
|
||||
fund.setFrozen(newFrozen);
|
||||
fund.setUpdateTime(updateTime);
|
||||
|
||||
// 记录流水
|
||||
assetService.createFlow(order.getUserId(), 2, order.getAmount(),
|
||||
balanceBefore, newBalance, "USDT", orderNo, "提现驳回退还");
|
||||
|
||||
System.out.println("[提现驳回成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
|
||||
", 退还金额: " + order.getAmount() + " USDT");
|
||||
} else {
|
||||
System.out.println("[充值驳回成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
|
||||
", 充值金额: " + order.getAmount() + " USDT, 驳回原因: " + rejectReason);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新订单状态 - 使用 LambdaUpdateWrapper 强制更新所有字段
|
||||
System.out.println("[FundService.approve] 步骤5: 更新订单状态...");
|
||||
System.out.println(" - 当前状态: " + order.getStatus());
|
||||
System.out.println(" - 目标状态: " + finalStatus);
|
||||
|
||||
// 更新订单状态
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
// 使用 LambdaUpdateWrapper 明确指定要更新的字段
|
||||
LambdaUpdateWrapper<OrderFund> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(OrderFund::getId, order.getId())
|
||||
.set(OrderFund::getStatus, finalStatus)
|
||||
.set(OrderFund::getApproveAdminId, adminId)
|
||||
.set(OrderFund::getApproveAdminName, adminName)
|
||||
.set(OrderFund::getApproveTime, now)
|
||||
.set(OrderFund::getConfirmTime, order.getConfirmTime())
|
||||
.set(OrderFund::getRejectReason, order.getRejectReason())
|
||||
.set(OrderFund::getAdminRemark, adminRemark)
|
||||
.set(OrderFund::getUpdateTime, now);
|
||||
|
||||
System.out.println(" - 准备执行数据库更新 (使用 LambdaUpdateWrapper)...");
|
||||
System.out.println(" - UPDATE SQL 将更新: status=" + finalStatus + ", approveAdminId=" + adminId);
|
||||
// 设置审批人信息
|
||||
if (adminRole == 3 || (adminRole == 1 && order.getStatus() == 5)) {
|
||||
// 财务审批/超管审批第二步
|
||||
updateWrapper.set(OrderFund::getFinanceAdminId, adminId)
|
||||
.set(OrderFund::getFinanceAdminName, adminName)
|
||||
.set(OrderFund::getFinanceApproveTime, now);
|
||||
// 保留第一步审批人信息不变
|
||||
updateWrapper.set(OrderFund::getAdminRemark, adminRemark);
|
||||
} else {
|
||||
// 管理员审批/超管审批第一步
|
||||
updateWrapper.set(OrderFund::getApproveAdminId, adminId)
|
||||
.set(OrderFund::getApproveAdminName, adminName)
|
||||
.set(OrderFund::getApproveTime, now)
|
||||
.set(OrderFund::getRejectReason, order.getRejectReason())
|
||||
.set(OrderFund::getAdminRemark, adminRemark);
|
||||
}
|
||||
|
||||
// 充值完成时设置confirmTime
|
||||
if (order.getType() == 1 && finalStatus == 3) {
|
||||
updateWrapper.set(OrderFund::getConfirmTime, now);
|
||||
}
|
||||
// 提现最终完成时设置confirmTime
|
||||
if (order.getType() == 2 && finalStatus == 2) {
|
||||
updateWrapper.set(OrderFund::getConfirmTime, now);
|
||||
}
|
||||
|
||||
int orderUpdateResult = orderFundMapper.update(null, updateWrapper);
|
||||
|
||||
if (orderUpdateResult <= 0) {
|
||||
throw new RuntimeException("订单更新失败");
|
||||
}
|
||||
|
||||
System.out.println("[审批完成] 订单号: " + orderNo + ", 订单类型: " + (order.getType() == 1 ? "充值" : "提现") +
|
||||
", 审批结果: " + (status == 2 ? "通过" : "驳回") + ", 最终状态: " + finalStatus +
|
||||
", 审批人: " + adminName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审批订单列表
|
||||
* 充值待确认(status=2) + 提现待审批(status=1)
|
||||
* 根据角色返回不同的待审批订单
|
||||
*/
|
||||
public IPage<OrderFund> getPendingOrders(Integer type, Integer status, int pageNum, int pageSize) {
|
||||
public IPage<OrderFund> getPendingOrders(Integer type, Integer status, int pageNum, int pageSize, Integer adminRole) {
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (type != null && type > 0) {
|
||||
// 指定类型
|
||||
if (adminRole != null && adminRole == 3) {
|
||||
// 财务:只看提现待财务审核(status=5)
|
||||
wrapper.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 5);
|
||||
} else 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));
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 1))
|
||||
.or(w -> w.eq(OrderFund::getType, 2).eq(OrderFund::getStatus, 5));
|
||||
}
|
||||
|
||||
wrapper.orderByAsc(OrderFund::getCreateTime);
|
||||
@@ -576,13 +526,39 @@ public class FundService {
|
||||
* 获取所有充提订单(管理员)
|
||||
*/
|
||||
public IPage<OrderFund> getAllOrders(Integer type, Integer status, int pageNum, int pageSize) {
|
||||
return getAllOrders(type, status, pageNum, pageSize, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有充提订单(按角色过滤)
|
||||
* role=2 普通管理员:看自己审批过的
|
||||
* role=3 财务:看提现订单 + 自己审批过的
|
||||
* adminId=null 超管:看所有
|
||||
*/
|
||||
public IPage<OrderFund> getAllOrders(Integer type, Integer status, int pageNum, int pageSize, Long adminId, Integer adminRole) {
|
||||
LambdaQueryWrapper<OrderFund> wrapper = new LambdaQueryWrapper<>();
|
||||
if (type != null && type > 0) {
|
||||
|
||||
if (adminRole != null && adminRole == 3) {
|
||||
// 财务只看提现订单
|
||||
wrapper.eq(OrderFund::getType, 2);
|
||||
} else if (type != null && type > 0) {
|
||||
wrapper.eq(OrderFund::getType, type);
|
||||
}
|
||||
|
||||
if (status != null && status > 0) {
|
||||
wrapper.eq(OrderFund::getStatus, status);
|
||||
}
|
||||
|
||||
if (adminId != null) {
|
||||
if (adminRole != null && adminRole == 3) {
|
||||
// 财务:看自己审批过的
|
||||
wrapper.eq(OrderFund::getFinanceAdminId, adminId);
|
||||
} else {
|
||||
// 管理员:看自己审批过的
|
||||
wrapper.eq(OrderFund::getApproveAdminId, adminId);
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(OrderFund::getCreateTime);
|
||||
|
||||
Page<OrderFund> page = new Page<>(pageNum, pageSize);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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.extension.service.impl.ServiceImpl;
|
||||
import com.it.rattan.monisuo.entity.AccountFund;
|
||||
import com.it.rattan.monisuo.entity.User;
|
||||
@@ -131,9 +132,11 @@ public class UserService extends ServiceImpl<UserMapper, User> {
|
||||
}
|
||||
|
||||
String token = JwtUtil.createToken(user.getId(), username, "user");
|
||||
user.setToken(token);
|
||||
user.setLastLoginTime(LocalDateTime.now());
|
||||
userMapper.updateById(user);
|
||||
// 只更新 token 和最后登录时间,避免 updateById 全字段更新
|
||||
userMapper.update(null, new LambdaUpdateWrapper<User>()
|
||||
.eq(User::getId, user.getId())
|
||||
.set(User::getToken, token)
|
||||
.set(User::getLastLoginTime, LocalDateTime.now()));
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("token", token);
|
||||
|
||||
@@ -25,6 +25,13 @@ public class JwtUtil {
|
||||
* 生成Token
|
||||
*/
|
||||
public static String createToken(Long userId, String username, String type) {
|
||||
return createToken(userId, username, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Token(带角色)
|
||||
*/
|
||||
public static String createToken(Long userId, String username, String type, Integer role) {
|
||||
Date now = new Date();
|
||||
Date expireDate = new Date(now.getTime() + EXPIRE_TIME);
|
||||
|
||||
@@ -32,15 +39,18 @@ public class JwtUtil {
|
||||
header.put("alg", "HS256");
|
||||
header.put("typ", "JWT");
|
||||
|
||||
return JWT.create()
|
||||
com.auth0.jwt.JWTCreator.Builder builder = JWT.create()
|
||||
.withHeader(header)
|
||||
.withIssuer(ISSUER)
|
||||
.withIssuedAt(now)
|
||||
.withExpiresAt(expireDate)
|
||||
.withClaim("userId", userId)
|
||||
.withClaim("username", username)
|
||||
.withClaim("type", type)
|
||||
.sign(Algorithm.HMAC256(SECRET));
|
||||
.withClaim("type", type);
|
||||
if (role != null) {
|
||||
builder = builder.withClaim("role", role);
|
||||
}
|
||||
return builder.sign(Algorithm.HMAC256(SECRET));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +99,18 @@ public class JwtUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色(user/admin对应的角色编号)
|
||||
*/
|
||||
public static Integer getRole(String token) {
|
||||
try {
|
||||
DecodedJWT jwt = verifyToken(token);
|
||||
return jwt.getClaim("role").asInt();
|
||||
} catch (JWTVerificationException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Token是否有效
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user