package modules.withdraw; import com.alibaba.fastjson.JSONObject; import com.jfinal.plugin.activerecord.Db; import common.jfinal.AppConfig; import common.model.BalanceLog; import common.model.User; import common.model.Withdraw; import common.utils.http.MyRet; import common.utils.hyg.HygSDK; import modules.order.OrderService; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class WithdrawService { // 提现手续费,750代表万分之750(7.5%) public static final long WITHDRAW_FEE = 750; public String hello() { return "Hello Withdraw"; } public MyRet create(long amount, User user) { long amountWithoutFee = amount * (10000 - WITHDRAW_FEE) / 10000; // 用户余额判断 if (user.getBalance() < (float)amount) { return MyRet.fail(String.format("提现失败,余额不足,你仅有%s而提现%s", user.getBalance(), amount)); } try { String orderSn = "DLTBH_WD_" + OrderService.generateOrderSn(); Db.tx(() -> { user.setBalance(user.getBalance() - (float)amount); if (!user.update()) { throw new RuntimeException("用户余额扣减失败"); } // 保存提现单据 Withdraw withdraw = new Withdraw(); withdraw.setWithdrawSn(orderSn); withdraw.setAmount((float)amount); withdraw.setStatus(10); withdraw.setUserId(user.getId()); withdraw.setCreateTime(System.currentTimeMillis()); withdraw.setUpdateTime(System.currentTimeMillis()); withdraw.setIsDeleted(0); // 保存资金流水 BalanceLog l = new BalanceLog(); l.set("create_time", System.currentTimeMillis()); l.set("is_deleted", 0); l.set("description", "用户提现:" + amount); // 描述中带上百分比 l.set("amount", -amount); l.set("user_id", user.getLong("id")); if (!l.save()) { throw new RuntimeException("资金流水记录创建失败: " + user.getId()); } if (!withdraw.save()) { throw new RuntimeException("提现单据保存失败"); } return true; }); float amountCNY = (float)amountWithoutFee / (float)100; float feePercent = (float)WITHDRAW_FEE / (float)100; return MyRet.ok(String.format("提现%s火花,实际到账%.2f(元),手续费%s%%,申请已提交", amount, amountCNY, feePercent)).setData(findByWithdrawSn(orderSn)); } catch (Exception e) { return MyRet.fail("提现申请失败: " + e.getMessage()); } } public MyRet pass(Withdraw withdraw, long approverId) { // 记录操作人 withdraw.setApproverId(approverId); try { User user = User.dao.findById(withdraw.getUserId()); long amountWithoutFee = (long) (withdraw.getAmount() * (10000 - WITHDRAW_FEE) / 10000); String workerId = user.getHygWorkerId(); String amountWithoutFeeStr = amountWithoutFee + ""; String hygPositionId = System.getenv("HYG_POSITION_ID"); AppConfig.LOGGER.info("{}, {}, {}, {}", workerId, amountWithoutFeeStr, withdraw.getWithdrawSn(), hygPositionId); JSONObject withdrawRst = HygSDK.singleDistribute(workerId, amountWithoutFeeStr, withdraw.getWithdrawSn(), hygPositionId); // 首先保证提现是成功的 if (!withdrawRst.getString("statusCode").equals("000000")) { withdraw.setHygOrigin(withdrawRst.toString()); withdraw.setReason("慧用工提现失败"); withdraw.setStatus(40); // 原子化操作user和withdraw的状态 Db.tx(() -> { // 把款项退给用户 user.setBalance(user.getBalance() + withdraw.getAmount()); if (!user.update()) { throw new RuntimeException("用户余额归还失败"); } if (!withdraw.update()) { throw new RuntimeException("单据状态更新失败"); } // 保存资金流水 BalanceLog l = new BalanceLog(); l.set("create_time", System.currentTimeMillis()); l.set("is_deleted", 0); l.set("description", "慧用工转款失败退还"); // 描述中带上百分比 l.set("amount", withdraw.getAmount()); l.set("user_id", user.getLong("id")); if (!l.save()) { throw new RuntimeException("资金流水记录创建失败: " + user.getId()); } return true; }); return MyRet.fail("慧用工提现失败:" + withdrawRst.getString("statusText")); } // 如果是成功提现 withdraw.setHygOrigin(withdrawRst.toString()); withdraw.setStatus(20); if (withdraw.update()) { return MyRet.ok("打款请求已发起").setData(withdraw); } else { return MyRet.fail("单据状态更新失败"); } } catch (Exception e) { return MyRet.fail("打款请求已发起失败: " + e.getMessage()); } } public MyRet refuse(Withdraw withdraw, String reason, long approverId) { User user = User.dao.findById(withdraw.getUserId()); try { Db.tx(() -> { user.setBalance(user.getBalance() + withdraw.getAmount()); if (!user.update()) { throw new RuntimeException("用户余额扣减失败"); } // 记录操作人 withdraw.setApproverId(approverId); withdraw.setReason(reason); withdraw.setStatus(40); if (!withdraw.update()) { throw new RuntimeException("提现单据保存失败"); } // 管理员拒绝 BalanceLog l = new BalanceLog(); l.set("create_time", System.currentTimeMillis()); l.set("is_deleted", 0); l.set("description", "管理员拒绝提现,请查看原因"); // 描述中带上百分比 l.set("amount", withdraw.getAmount()); l.set("user_id", user.getLong("id")); if (!l.save()) { throw new RuntimeException("资金流水记录创建失败: " + user.getId()); } return true; }); return MyRet.ok("该单据已拒绝").setData(withdraw); } catch (Exception e) { return MyRet.fail("拒绝失败:" + e.getMessage()); } } public MyRet list(int pageNumber, int pageSize, Integer status, Long userId) { // 接收 Integer 类型的 orderStatus // 构建用于查询当前页订单列表的 SQL String columns = " * "; String select = "SELECT " + columns; StringBuilder fromWhere = new StringBuilder("FROM t_withdraw"); // 基础 from 子句 String orderBy = "ORDER BY update_time DESC"; boolean hasWhereClause = false; // 标志位,用于判断是否已经添加了 WHERE 关键字 List params = new ArrayList<>(); // 用于 SELECT 列表查询的参数 // ✅ 根据 status 筛选 if (status != null && status >= 0) { // status 通常是枚举,>=0 可能是有效的判断 fromWhere.append(hasWhereClause ? " AND" : " WHERE").append(" status = ?"); params.add(status); hasWhereClause = true; } // ✅ 根据 userId 筛选 // 通常 userId 是正整数,如果 userId 可能为 0 需要特别对待 if (userId != null && userId > 0) { // 通常 userId 从 1 开始,或者根据你的业务逻辑设定 fromWhere.append(hasWhereClause ? " AND" : " WHERE").append(" user_id = ?"); params.add(userId); hasWhereClause = true; } // 计算 LIMIT 的 offset (偏移量) int offset = (pageNumber - 1) * pageSize; // 构建查询当前页订单列表的最终 SQL (手动添加 LIMIT) String listSql = select + " " + fromWhere + " " + orderBy + " LIMIT ?, ?"; params.add(offset); params.add(pageSize); List withdrawList = Withdraw.dao.find(listSql, params.toArray()); // 如果列表为空,直接返回 if (withdrawList.isEmpty()) { Map response = new HashMap<>(); // 准备返回结构 response.put("list", new ArrayList<>()); response.put("total_row", 0); response.put("total_page", 0); response.put("page_size", pageSize); response.put("page_number", pageNumber); response.put("total_order_count", 0); // 与你的 total_user_count 对应 return MyRet.ok("查询成功").setData(response); } // 获取符合条件的条目数量 long totalRowLong = countWithdraws(status, userId); // 手动计算 total_page int totalPage = (int) Math.ceil((double) totalRowLong / pageSize); if (totalPage == 0 && totalRowLong > 0) { // 解决总行数大于0但总页数为0(pageSize大于totalRow)的问题 totalPage = 1; } // 封装最终响应 Map response = new HashMap<>(); response.put("list", withdrawList); response.put("total_row", totalRowLong); // 符合条件的订单总数 response.put("total_page", totalPage); // 手动计算的总页数 response.put("page_size", pageSize); response.put("page_number", pageNumber); response.put("total_order_count", totalRowLong); // 对应 total_user_count,这里是符合订单状态筛选的总数 return MyRet.ok("查询成功").setData(response); } public long countWithdraws(Integer status, Long userId) { // 接收 Integer 类型的 status StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM t_withdraw"); // 使用 COUNT(*),更通用 List params = new ArrayList<>(); boolean hasWhereClause = false; // 标志位,用于判断是否已经添加了 WHERE 关键字 // ✅ 根据 status 筛选 if (status != null && status >= 0) { // status 通常是枚举,>=0 可能是有效的判断 sql.append(hasWhereClause ? " AND" : " WHERE").append(" status = ?"); params.add(status); hasWhereClause = true; } // ✅ 根据 userId 筛选 // 通常 userId 是正整数,如果 userId 可能为 0 需要特别对待 if (userId != null && userId > 0) { // 通常 userId 从 1 开始,或者根据你的业务逻辑设定 sql.append(hasWhereClause ? " AND" : " WHERE").append(" user_id = ?"); params.add(userId); hasWhereClause = true; } // 执行查询并返回结果 // Db.queryLong() 如果没有结果会返回 null,如果查询结果行数为 0,则返回 Long 0 // 这符合 COUNT() 函数的预期 return Db.queryLong(sql.toString(), params.toArray()); } public Withdraw findByWithdrawSn(String sn) { return Withdraw.dao.findFirst("SELECT * FROM t_withdraw WHERE withdraw_sn=?", sn); } }