skyfffire пре 1 месец
родитељ
комит
425a5455cc

+ 75 - 0
src/main/java/common/utils/IpAddressUtil.java

@@ -0,0 +1,75 @@
+package common.utils;
+
+import com.jfinal.core.Controller;
+import com.jfinal.kit.StrKit;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class IpAddressUtil {
+    /**
+     * 在 Controller 中获取请求方的公网 IP 地址
+     * 考虑了多层代理(CDN, 负载均衡器, 反向代理等)的情况
+     *
+     * @param controller JFinal的Controller实例
+     * @return 客户端公网IP地址,如果获取失败则返回 null 或空字符串
+     */
+    public static String getClientIpAddress(Controller controller) {
+        return getClientIpAddress(controller.getRequest());
+    }
+
+    /**
+     * 获取请求方的公网 IP 地址
+     * 考虑了多层代理(CDN, 负载均衡器, 反向代理等)的情况
+     *
+     * @param request HttpServletRequest 请求对象
+     * @return 客户端公网IP地址,如果获取失败则返回 null 或空字符串
+     */
+    public static String getClientIpAddress(HttpServletRequest request) {
+        if (request == null) {
+            return null;
+        }
+
+        // 顺序检查各种可能的代理Header
+        String ip = request.getHeader("X-Forwarded-For");
+        if (StrKit.notBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
+            // X-Forwarded-For 可能包含多个 IP 地址,用逗号分隔,第一个通常是真实客户端IP
+            int commaOffset = ip.indexOf(',');
+            if (commaOffset > 0) {
+                ip = ip.substring(0, commaOffset);
+            }
+            return ip.trim();
+        }
+
+        ip = request.getHeader("Proxy-Client-IP");
+        if (StrKit.notBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
+            return ip.trim();
+        }
+
+        ip = request.getHeader("WL-Proxy-Client-IP");
+        if (StrKit.notBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
+            return ip.trim();
+        }
+
+        ip = request.getHeader("HTTP_CLIENT_IP");
+        if (StrKit.notBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
+            return ip.trim();
+        }
+
+        ip = request.getHeader("HTTP_X_FORWARDED_FOR"); // 有些代理会使用此Header
+        if (StrKit.notBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
+            int commaOffset = ip.indexOf(',');
+            if (commaOffset > 0) {
+                ip = ip.substring(0, commaOffset);
+            }
+            return ip.trim();
+        }
+
+        // 最终回退到 Servlet API 的默认获取方式
+        // 这个通常是直接连接到你应用服务器的代理服务器的IP,而不是客户端真实IP
+        ip = request.getRemoteAddr();
+        return ip;
+    }
+
+    // 考虑到可能会有 IPv6 地址,但通常情况下我们关心 IPv4。
+    // 如果需要专门处理 IPv6,可以在匹配 IP 地址时进行正则判断。
+}

+ 31 - 0
src/main/java/modules/deposit/DepositController.java

@@ -1,13 +1,18 @@
 package modules.deposit;
 
+import com.alibaba.fastjson.JSONObject;
+import com.jfinal.aop.Before;
 import com.jfinal.aop.Inject;
 import com.jfinal.json.Json;
 import com.jfinal.kit.HttpKit;
 import com.jfinal.kit.StrKit;
 import com.wechat.pay.java.service.payments.model.Transaction;
+import common.interceptor.LoginInterceptor;
+import common.interceptor.empty.EmptyInterface;
 import common.jfinal.AppConfig;
 import common.model.Deposit;
 import common.model.User;
+import common.utils.IpAddressUtil;
 import common.utils.http.MyController;
 import common.utils.http.MyRet;
 import common.utils.wechat.WeChatService;
@@ -125,7 +130,33 @@ public class DepositController extends MyController {
     }
     
     // 创建微信支付订单
+    @Before(LoginInterceptor.class)
+    @EmptyInterface({"amount"})
     public void create() {
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+
+        // 获取支付数量(分)
+        String amountStr = requestBodyJson.getString("amount");
+        int amount;
+        try {
+            amount = Integer.parseInt(amountStr);
+
+            if (amount <= 0) {
+                renderJson(MyRet.fail("支付数量(amount)期待是正整数,你传的是: " + amountStr));
+                return;
+            }
+        } catch (Exception e) {
+            renderJson(MyRet.fail("支付数量(amount)格式不正确: " + e.getMessage()));
+            return;
+        }
+        
+        // 获取公网ip地址
+        String ip = IpAddressUtil.getClientIpAddress(this);
+        
+        // 获取当前用户
+        User user = userService.findUserByMobileNumber(getSessionAttr("mobile_number"));
         
+        // 创建并返回支付链接给前端
+        renderJson(service.createDepositOrder(amount, ip, user.getId()));
     }
 }

+ 42 - 0
src/main/java/modules/deposit/DepositService.java

@@ -1,12 +1,54 @@
 package modules.deposit;
 
+import com.wechat.pay.java.service.payments.h5.model.H5Info;
+import com.wechat.pay.java.service.payments.h5.model.PrepayResponse;
+import com.wechat.pay.java.service.payments.h5.model.SceneInfo;
 import common.model.Deposit;
+import common.utils.http.MyRet;
+
+import static common.utils.wechat.WeChatService.prepay;
 
 public class DepositService {
     public String hello() {
         return "Hello deposit";
     }
     
+    public MyRet createDepositOrder(Integer amount, String ip, long userId) {
+        String notifyUrl = System.getenv("URL_BASE") + "/deposit/deposited";
+        String outTradeNo = "DLTBH_WX_" + System.currentTimeMillis() + "_" + userId;
+
+        // 场景信息
+        H5Info h5Info = new H5Info();
+        h5Info.setType("Wrp"); // 使用H5支付的场景:Wap、iOS、Android
+
+        SceneInfo sceneInfo = new SceneInfo();
+        sceneInfo.setPayerClientIp(ip);
+        sceneInfo.setH5Info(h5Info);
+
+        // 方便获取报错信息
+        try {
+            // 获取微信支付订单结果
+            PrepayResponse response = prepay(amount, outTradeNo, notifyUrl, sceneInfo);
+            
+            // 微信支付订单创建成功,创建内部订单
+            Deposit deposit = new Deposit();
+            
+            // put各类参数
+            deposit.setUserId(userId);
+            deposit.setWxOrderNo(outTradeNo);
+            deposit.setAmount(amount);
+            deposit.setCreateTime(System.currentTimeMillis());
+            
+            if (deposit.save()) {
+                return MyRet.ok("微信支付订单创建成功").setData(response.getH5Url());
+            } else {
+                return MyRet.fail("微信支付订单创建失败:Deposit入库失败").setData(deposit);
+            }
+        } catch (Exception e) {
+            return MyRet.fail("微信支付订单创建失败").setData(e.getMessage());
+        }
+    }
+    
     public Deposit findDepositByWxOrderNo(String wxOrderNo) {
         return Deposit.dao.findFirst("SELECT * FROM t_deposit WHERE wx_order_no=?", wxOrderNo);
     }

+ 45 - 0
src/test/rest/DepositControllerTest.http

@@ -0,0 +1,45 @@
+### 连通性测试
+POST {{ baseUrl }}/deposit/hello
+
+### 用户发起支付
+POST {{ baseUrl }}/deposit/create
+Content-Type: application/json
+dl-token: {{dl_token_var}}
+
+{
+  "amount": 10
+}
+
+### 获取用户自己的充值订单
+POST {{ baseUrl }}/deposit/deposits
+Content-Type: application/json
+dl-token: {{dl_token_var}}
+
+{
+  "page_number": 1,
+  "page_size": 100
+}
+### 返回值解释
+### list 分页结果的实际列表数据 
+### page_number 当前页码
+### page_size 每页大小
+### total_page 总页数
+### total_row  当前分页的总数
+### total_deposit_count 符合条件的总记录数
+
+### 【超级管理员】获取所有用户的充值订单
+POST {{ baseUrl }}/deposit/depositsByAdmin
+Content-Type: application/json
+dl-token: {{dl_token_var}}
+
+{
+  "page_number": 1,
+  "page_size": 100
+}
+### 返回值解释
+### list 分页结果的实际列表数据 
+### page_number 当前页码
+### page_size 每页大小
+### total_page 总页数
+### total_row  当前分页的总数
+### total_deposit_count 符合条件的总记录数

+ 8 - 9
src/test/rest/UserControllerTest.http

@@ -1,15 +1,6 @@
 ### 连通性测试
 POST {{ baseUrl }}/user/hello
 
-### 慧用工免登请求地址,用来换取手机号
-POST {{ baseUrl }}/user/tokenToMobileNumber
-Content-Type: application/json
-
-{
-  "token": "-6438275617003453201"
-}
-
-
 
 ### 登录,可以选择传入pwd_md5(密码登录)或verify_code(验证码登录)。如果两者都传入了,后台会优先使用pwd_md5
 ### 登录成功后会返token回来,后面请求需要用户状态的接口都需要携带到headers里面,key是dl-token
@@ -27,6 +18,14 @@ Content-Type: application/json
     console.log("dl_token_var set to: " + client.global.get("dl_token_var"));
 %}
 
+### 慧用工免登请求地址,用来换取手机号
+POST {{ baseUrl }}/user/tokenToMobileNumber
+Content-Type: application/json
+
+{
+  "token": "-6438275617003453201"
+}
+
 
 ### 发送验证码
 POST {{ baseUrl }}/user/sendVerifyCode