skyfffire 1 mesiac pred
rodič
commit
e6408965b3

+ 4 - 2
src/main/java/common/jfinal/AppConfig.java

@@ -110,8 +110,10 @@ public class AppConfig extends JFinalConfig {
         TaskScheduler.init(asyncTaskExecutor);
         
         // 数据维护线程
-        TaskScheduler.submit(new OrderStatusMaintenanceTask(new OrderService(), new UserService()));
-        TaskScheduler.submit(new NfttStatusMaintenanceTask());
+        if (DEV_MODE.equals("0")) {
+            TaskScheduler.submit(new OrderStatusMaintenanceTask(new OrderService(), new UserService()));
+            TaskScheduler.submit(new NfttStatusMaintenanceTask());
+        }
     }
 
     @Override

+ 9 - 9
src/main/java/common/model/base/BaseWithdraw.java

@@ -24,15 +24,15 @@ public abstract class BaseWithdraw<M extends BaseWithdraw<M>> extends Model<M> i
 	/**
 	 * 提现单位,1=1火花/分
 	 */
-	public void setAmount(java.lang.String amount) {
+	public void setAmount(java.lang.Float amount) {
 		set("amount", amount);
 	}
 	
 	/**
 	 * 提现单位,1=1火花/分
 	 */
-	public java.lang.String getAmount() {
-		return getStr("amount");
+	public java.lang.Float getAmount() {
+		return getFloat("amount");
 	}
 	/**
 	 * 10待审批 20审批通过等待放款 30提现完成 40提现被拒绝
@@ -50,15 +50,15 @@ public abstract class BaseWithdraw<M extends BaseWithdraw<M>> extends Model<M> i
 	/**
 	 * 提现发起者
 	 */
-	public void setUserId(java.lang.Integer userId) {
+	public void setUserId(java.lang.Long userId) {
 		set("user_id", userId);
 	}
 	
 	/**
 	 * 提现发起者
 	 */
-	public java.lang.Integer getUserId() {
-		return getInt("user_id");
+	public java.lang.Long getUserId() {
+		return getLong("user_id");
 	}
 	/**
 	 * 说明
@@ -76,15 +76,15 @@ public abstract class BaseWithdraw<M extends BaseWithdraw<M>> extends Model<M> i
 	/**
 	 * 审批人
 	 */
-	public void setApproverId(java.lang.Integer approverId) {
+	public void setApproverId(java.lang.Long approverId) {
 		set("approver_id", approverId);
 	}
 	
 	/**
 	 * 审批人
 	 */
-	public java.lang.Integer getApproverId() {
-		return getInt("approver_id");
+	public java.lang.Long getApproverId() {
+		return getLong("approver_id");
 	}
 	/**
 	 * 慧用工原文

+ 3 - 3
src/main/java/common/utils/hyg/HygSDK.java

@@ -59,7 +59,7 @@ public class HygSDK {
         JSONObject workerDetails = findWorkerDetails(workerId);
         // 先获取个体账户信息
         if (!workerDetails.getString("statusCode").equals("000000")) {
-            workerDetails.put("statusText", "个体账户信息获取失败:" + workerDetails.getString("statusText"));
+            workerDetails.put("statusText", "个体账户信息获取失败:" + workerDetails.getString("statusText") + ", workerId=" + workerId);
 
             return workerDetails;
         }
@@ -165,12 +165,12 @@ public class HygSDK {
              */
             // String orderSn = OrderService.generateOrderSn();
             // AppConfig.LOGGER.info("order_sn = {}", orderSn);
-            // AppConfig.LOGGER.info(HygSDK.singleDistribute("W1420857775537291264", "1000", orderSn, "P1421138295391289344").toString());
+            // AppConfig.LOGGER.info(HygSDK.singleDistribute("W1420857775537291264", "925", "DLTBH_WD_17588741472336835", "P1421138295391289344").toString());
             
             // 一次至少提现1000分
             // {"data":{"valueAddedTax":"0","distributeStatus":75,"incomeActualAmount":0,"constructionTax":"0","localEducationSupplementaryTax":"0","educationSurchargeTax":"0","remark":"计费标准超出范围","requestNo":"17588687904896652","individualIncomeTax":"0","incomeTaxAmount":0,"serviceCharge":"0","distributeId":"BL1421144242402639872","createTime":"2025-09-26 14:39:51","useCouponAmount":0,"distributeAmount":"100"},"statusText":"success","statusCode":"000000"}
             // {"data":{"valueAddedTax":"0","distributeStatus":60,"incomeActualAmount":1000,"constructionTax":"0","localEducationSupplementaryTax":"0","payTime":"2025-09-26 14:44:55","educationSurchargeTax":"0","remark":"","requestNo":"17588690942918042","individualIncomeTax":"0","incomeTaxAmount":0,"serviceCharge":"75","distributeId":"BL1421145516409892864","createTime":"2025-09-26 14:44:55","useCouponAmount":0,"bankSerialNo":"2011000420250926143698976426","distributeAmount":"1000"},"statusText":"success","statusCode":"000000"}
-            // AppConfig.LOGGER.info(HygSDK.signalQuery("17588690942918042").toString());
+            // AppConfig.LOGGER.info(HygSDK.signalQuery("DLTBH_WD_17588741472336835").toString());
         } catch (Exception e) {
             e.printStackTrace();
         }

+ 63 - 0
src/main/java/modules/withdraw/WithdrawController.java

@@ -1,18 +1,81 @@
 package modules.withdraw;
 
+import com.alibaba.fastjson.JSONObject;
 import com.jfinal.aop.Before;
 import com.jfinal.aop.Inject;
+import com.jfinal.kit.StrKit;
 import common.interceptor.LoginInterceptor;
+import common.interceptor.empty.EmptyInterface;
+import common.interceptor.role.RequiredRoleInterface;
+import common.model.User;
+import common.model.Withdraw;
 import common.utils.http.MyController;
 import common.utils.http.MyRet;
+import modules.user.UserController;
+import modules.user.UserService;
 
 public class WithdrawController extends MyController {
     @Inject
     WithdrawService service;
+    @Inject
+    UserService userService;
     
     public void hello() {
         renderJson(MyRet.ok(service.hello()));
     }
     
+    @Before(LoginInterceptor.class)
+    @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
+    @EmptyInterface({"amount"})
+    public void create() {
+        // 通过 MyController 获取解析后的 JSON 对象,拦截器也使用了这个方法
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+
+        // 提现数量解析
+        String amountStr = requestBodyJson.getString("amount");
+        long amount;
+        try {
+            amount = Long.parseLong(amountStr);
+
+            if (amount < 1100) {
+                renderJson(MyRet.fail("至少要提现1100火花!"));
+                return;
+            }
+        } catch (Exception e) {
+            renderJson(MyRet.fail("提现数量(amount)格式不正确: " + e.getMessage()));
+            return;
+        }
+
+        User user = userService.findUserByMobileNumber(getSessionAttr("mobile_number"));
+        if (StrKit.isBlank(user.getHygWorkerId())) {
+            renderJson(MyRet.fail("请先进行实名认证").setCode(MyRet.CODE_NO_AUTH));
+            return;
+        }
+        
+        renderJson(service.create(amount, user));
+    }
     
+    @Before(LoginInterceptor.class)
+    @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
+    @EmptyInterface({"withdraw_sn"})
+    public void pass() {
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+        
+        String withdraw_sn = requestBodyJson.getString("withdraw_sn");
+        
+        if (StrKit.isBlank(withdraw_sn)) {
+            renderJson(MyRet.fail("withdraw_sn 不能为空"));
+            return;
+        }
+        Withdraw withdraw = Withdraw.dao.findFirst("SELECT * FROM t_withdraw WHERE withdraw_sn=?", withdraw_sn);
+        if (withdraw == null) {
+            renderJson(MyRet.fail("withdraw_sn 找不到对应的单据"));
+            return;
+        }
+        
+        // 获取操作人的id
+        long approverId = userService.findUserByMobileNumber(getSessionAttr("mobile_number")).getId();
+        
+        renderJson(service.pass(withdraw, approverId));
+    }
 }

+ 107 - 0
src/main/java/modules/withdraw/WithdrawService.java

@@ -1,7 +1,114 @@
 package modules.withdraw;
 
+import com.alibaba.fastjson.JSONObject;
+import com.jfinal.plugin.activerecord.Db;
+import common.jfinal.AppConfig;
+import common.model.User;
+import common.model.Withdraw;
+import common.utils.http.MyRet;
+import common.utils.hyg.HygSDK;
+import modules.order.OrderService;
+
 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.setState(10);
+                withdraw.setUserId(user.getId());
+                withdraw.setCreateTime(System.currentTimeMillis());
+                withdraw.setUpdateTime(System.currentTimeMillis());
+                withdraw.setIsDeleted(0);
+                
+                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));
+        } 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.setState(40);
+                
+                // 原子化操作user和withdraw的状态
+                Db.tx(() -> {
+                    // 把款项退给用户
+                    user.setBalance(user.getBalance() + withdraw.getAmount());
+                    if (!user.update()) {
+                        throw new RuntimeException("用户余额归还失败");
+                    }
+
+                    if (!withdraw.update()) {
+                        throw new RuntimeException("单据状态更新失败");
+                    }
+                    
+                    return true;
+                });
+                
+                return MyRet.fail("慧用工提现失败:" + withdrawRst.getString("statusText"));
+            }
+            
+            // 如果是成功提现
+            withdraw.setHygOrigin(withdrawRst.toString());
+            withdraw.setState(20);
+            if (withdraw.update()) {
+                return MyRet.ok("打款请求已发起");
+            } else {
+                return MyRet.ok("单据状态更新失败");
+            }
+        } catch (Exception e) {
+            return MyRet.fail("打款请求已发起失败: " + e.getMessage());
+        }
+    }
 }

+ 10 - 1
src/test/rest/WithdrawControllerTest.http

@@ -7,6 +7,15 @@ Content-Type: application/json
 dl-token: {{dl_token_var}}
 
 {
-  "amount": 100
+  "amount": 1100
 }
 
+### 【超级管理员】 通过提现审批并给用户打款
+### 对接之前一定要让管理员多确认一次,不要点pass就直接提交到后台来了,确认给他放款(amount/100)元
+POST {{ baseUrl }}/withdraw/pass
+Content-Type: application/json
+dl-token: {{dl_token_var}}
+
+{
+  "withdraw_sn": "DLTBH_WD_17588746359625898"
+}