Browse Source

订单列表增加取消以及支付功能

zk 1 year ago
parent
commit
6b422fb59c

+ 39 - 1
app/src/main/java/com/datarecovery/master/data/api/bean/OrderBean.java

@@ -2,6 +2,8 @@ package com.datarecovery.master.data.api.bean;
 
 import androidx.annotation.IntDef;
 
+import com.atmob.common.runtime.ContextUtil;
+import com.datarecovery.master.R;
 import com.datarecovery.master.module.member.MemberType;
 import com.datarecovery.master.utils.DateUtil;
 import com.google.gson.annotations.SerializedName;
@@ -19,6 +21,9 @@ public class OrderBean {
     private String outTradeNo;
     @SerializedName("id")
     private int id;
+
+    @SerializedName("itemId")
+    private int itemId;
     @SerializedName("authCode")
     private String authCode;
 
@@ -34,6 +39,9 @@ public class OrderBean {
     @SerializedName("tutorialUrl")
     private String tutorialLink;
 
+    @SerializedName("createTime")
+    private String createTime;
+
     @SerializedName("payMethod")
     private int payMethod;
 
@@ -44,6 +52,10 @@ public class OrderBean {
     @SerializedName("status")
     private int status;
 
+    @SerializedName("expirationDesc")
+    private String expirationDesc;
+
+
     @IntDef({OrderStatus.SUCCESS, OrderStatus.TODO})
     @Retention(RetentionPolicy.SOURCE)
     public @interface OrderStatus {
@@ -51,6 +63,17 @@ public class OrderBean {
         int TODO = 2;
     }
 
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public String getExpirationDesc() {
+        return expirationDesc;
+    }
+
+    public int getItemId() {
+        return itemId;
+    }
 
     public int getPayMethod() {
         return payMethod;
@@ -93,6 +116,10 @@ public class OrderBean {
         return String.valueOf(amount / 100.0);
     }
 
+    public int getFenAmount() {
+        return amount;
+    }
+
     public void setAmount(int amount) {
         this.amount = amount;
     }
@@ -146,6 +173,17 @@ public class OrderBean {
     }
 
     public boolean isShowLink() {
-        return Objects.equals(auths, MemberType.APP_WX_FRIEND_RECOVER) || Objects.equals(auths, MemberType.APP_WX_MESSAGE_RECOVER) || Objects.equals(auths, MemberType.APP_SUPER_RECOVER);
+        return (Objects.equals(auths, MemberType.APP_WX_FRIEND_RECOVER) || Objects.equals(auths, MemberType.APP_WX_MESSAGE_RECOVER) || Objects.equals(auths, MemberType.APP_SUPER_RECOVER)) && status == OrderStatus.SUCCESS;
+    }
+
+    public String getOrderStatusDesc() {
+        switch (status) {
+            case OrderStatus.SUCCESS:
+                return ContextUtil.getContext().getString(R.string.order_success);
+            case OrderStatus.TODO:
+                return ContextUtil.getContext().getString(R.string.order_todo_pay);
+            default:
+                return "";
+        }
     }
 }

+ 125 - 0
app/src/main/java/com/datarecovery/master/data/api/bean/PayOptions.java

@@ -0,0 +1,125 @@
+package com.datarecovery.master.data.api.bean;
+
+import android.util.Pair;
+
+import com.datarecovery.master.module.member.MemberType;
+
+import atmob.reactivex.rxjava3.disposables.Disposable;
+
+public class PayOptions {
+
+
+    AddDisposable disposable;
+    int goodsId;
+    String goodsName;
+    int amount;
+    int payPlatform;
+    int payMethod;
+    boolean isPermanent;
+    @MemberType
+    String memberType;
+
+    private ShowLoadingEvent showLoadingEvent;
+    private OnSubscribeSuccess onSubscribeSuccess;
+    private QRCodeEvent wxQrCodeEvent;
+    private Pair<String, String> wxQrCode;
+    private QRCodeEvent alQrCodeEvent;
+    private Pair<String, String> alQrCode;
+
+    public PayOptions(AddDisposable disposable, int goodsId, String goodsName, int amount, int payPlatform, int payMethod, boolean isPermanent, String memberType, ShowLoadingEvent showLoadingEvent, OnSubscribeSuccess onSubscribeSuccess, QRCodeEvent wxQrCodeEvent, QRCodeEvent alQrCodeEvent) {
+        this.disposable = disposable;
+        this.goodsId = goodsId;
+        this.goodsName = goodsName;
+        this.amount = amount;
+        this.payPlatform = payPlatform;
+        this.payMethod = payMethod;
+        this.isPermanent = isPermanent;
+        this.memberType = memberType;
+        this.showLoadingEvent = showLoadingEvent;
+        this.onSubscribeSuccess = onSubscribeSuccess;
+        this.wxQrCodeEvent = wxQrCodeEvent;
+        this.alQrCodeEvent = alQrCodeEvent;
+    }
+
+    public Pair<String, String> getWxQrCode() {
+        return wxQrCode;
+    }
+
+    public Pair<String, String> getAlQrCode() {
+        return alQrCode;
+    }
+
+    public boolean isPermanent() {
+        return isPermanent;
+    }
+
+    public AddDisposable getDisposable() {
+        return disposable;
+    }
+
+    public int getGoodsId() {
+        return goodsId;
+    }
+
+    public String getGoodsName() {
+        return goodsName;
+    }
+
+    public int getAmount() {
+        return amount;
+    }
+
+    public int getPayPlatform() {
+        return payPlatform;
+    }
+
+    public int getPayMethod() {
+        return payMethod;
+    }
+
+    public String getMemberType() {
+        return memberType;
+    }
+
+
+    public void addDisposable(Disposable d) {
+        if (disposable == null) {
+            return;
+        }
+        disposable.add(d);
+    }
+
+    public void setShowLoadingEvent(boolean isShow) {
+        if (showLoadingEvent != null) showLoadingEvent.showLoadingEvent(isShow);
+    }
+
+    public void setOnSubscribeSuccessEvent(String orderId) {
+        if (onSubscribeSuccess != null) onSubscribeSuccess.onSubscribeSuccess(orderId);
+    }
+
+    public void setShowWxQRPaymentEvent(Pair<String, String> pair) {
+        this.wxQrCode = pair;
+        if (wxQrCodeEvent != null) wxQrCodeEvent.showQRCode(this);
+    }
+
+    public void setShowAlQRPaymentEvent(Pair<String, String> pair) {
+        this.alQrCode = pair;
+        if (alQrCodeEvent != null) alQrCodeEvent.showQRCode(this);
+    }
+
+    public interface QRCodeEvent {
+        void showQRCode(PayOptions payOptions);
+    }
+
+    public interface AddDisposable {
+        void add(Disposable disposable);
+    }
+
+    public interface ShowLoadingEvent {
+        void showLoadingEvent(boolean isShow);
+    }
+
+    public interface OnSubscribeSuccess {
+        void onSubscribeSuccess(String orderId);
+    }
+}

+ 187 - 1
app/src/main/java/com/datarecovery/master/data/repositories/MemberRepository.java

@@ -1,25 +1,50 @@
 package com.datarecovery.master.data.repositories;
 
 
+import android.util.Pair;
+
+import com.atmob.common.runtime.ActivityUtil;
+import com.datarecovery.master.R;
 import com.datarecovery.master.data.api.AtmobApi;
+import com.datarecovery.master.data.api.bean.PayOptions;
+import com.datarecovery.master.data.api.bean.WechatPaymentSignBean;
 import com.datarecovery.master.data.api.request.MemberDetailRequest;
 import com.datarecovery.master.data.api.request.MemberPayRequest;
 import com.datarecovery.master.data.api.request.PaymentStatusRequest;
 import com.datarecovery.master.data.api.response.MemberDetailResponse;
 import com.datarecovery.master.data.api.response.MemberPayResponse;
 import com.datarecovery.master.data.api.response.PaymentStatusResponse;
+import com.datarecovery.master.data.consts.Constants;
+import com.datarecovery.master.data.consts.ErrorCode;
+import com.datarecovery.master.data.consts.EventId;
+import com.datarecovery.master.handler.EventHelper;
+import com.datarecovery.master.module.login.LoginActivity;
 import com.datarecovery.master.module.member.MemberType;
 import com.datarecovery.master.utils.BoxingUtil;
+import com.datarecovery.master.utils.Maps;
 import com.datarecovery.master.utils.OrderReportHelper;
+import com.datarecovery.master.utils.ReportUtil;
 import com.datarecovery.master.utils.RxHttpHandler;
+import com.datarecovery.master.utils.ToastUtil;
+import com.google.gson.Gson;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import atmob.reactivex.rxjava3.annotations.NonNull;
 import atmob.reactivex.rxjava3.core.Single;
+import atmob.reactivex.rxjava3.core.SingleObserver;
+import atmob.reactivex.rxjava3.disposables.Disposable;
 import atmob.rxjava.utils.RxJavaUtil;
+import plus.pay.AgilePay;
+import plus.pay.alipay.AlipayInfo;
+import plus.pay.listener.AgilePayState;
+import plus.pay.wxpay.WXpayInfo;
 
 @Singleton
 public class MemberRepository {
@@ -27,11 +52,171 @@ public class MemberRepository {
 
     private final AtmobApi atmobApi;
     private final DeviceFuncRepository deviceFuncRepository;
+    private final Gson gson;
+    private final PayRepository payRepository;
 
     @Inject
-    public MemberRepository(AtmobApi atmobApi, DeviceFuncRepository deviceFuncRepository) {
+    public MemberRepository(AtmobApi atmobApi, DeviceFuncRepository deviceFuncRepository, PayRepository payRepository, Gson gson) {
         this.atmobApi = atmobApi;
         this.deviceFuncRepository = deviceFuncRepository;
+        this.payRepository = payRepository;
+        this.gson = gson;
+    }
+
+    public void payOrder(@NonNull PayOptions payOptions) {
+        if (payOptions == null) {
+            throw new NullPointerException("payOptions is null");
+        }
+        requestPayOrder(payOptions.getGoodsId(), payOptions.getPayPlatform(), payOptions.getPayMethod()).subscribe(new SingleObserver<MemberPayResponse>() {
+            @Override
+            public void onSubscribe(@NonNull Disposable d) {
+                payOptions.addDisposable(d);
+                payOptions.setShowLoadingEvent(true);
+            }
+
+            @Override
+            public void onSuccess(@NonNull MemberPayResponse memberPayResponse) {
+                payOptions.setShowLoadingEvent(false);
+                if (payOptions.getPayPlatform() == 1 && payOptions.getPayMethod() == 2) {
+                    onWeChatPay(memberPayResponse.getWechatPayPrepayJson(), memberPayResponse.getOutTradeNo(), payOptions);
+                } else if (payOptions.getPayPlatform() == 4 && payOptions.getPayMethod() == 2) {
+                    onWeChatScanPay(memberPayResponse.getWechatPayPrepayJson(), memberPayResponse.getOutTradeNo(), payOptions);
+                } else if (payOptions.getPayPlatform() == 1 && payOptions.getPayMethod() == 1) {
+                    onAliPay(memberPayResponse.getAlipayOrderString(), memberPayResponse.getOutTradeNo(), payOptions);
+                } else if (payOptions.getPayPlatform() == 4 && payOptions.getPayMethod() == 1) {
+                    onAliPayScan(memberPayResponse.getAlipayQrcodeHtml(), memberPayResponse.getOutTradeNo(), payOptions);
+                }
+            }
+
+            @Override
+            public void onError(@NonNull Throwable throwable) {
+                throwable.printStackTrace();
+                payOptions.setShowLoadingEvent(false);
+                if (throwable instanceof RxHttpHandler.ServerErrorException) {
+                    RxHttpHandler.ServerErrorException serverErrorException = (RxHttpHandler.ServerErrorException) throwable;
+                    if (serverErrorException.getCode() == ErrorCode.ERROR_CODE_NO_LOGIN_ERROR) {
+                        ToastUtil.show(R.string.no_login, ToastUtil.LENGTH_SHORT);
+                        LoginActivity.start(ActivityUtil.getTopActivity(), ReportUtil.getReportId(payOptions.getMemberType()));
+                    } else {
+                        ToastUtil.show(serverErrorException.getMsg(), ToastUtil.LENGTH_SHORT);
+                    }
+                } else {
+                    ToastUtil.show(R.string.member_payment_failed, ToastUtil.LENGTH_SHORT);
+                }
+            }
+        });
+    }
+
+    private void onAliPayScan(String s, String orderId, @NonNull PayOptions payOptions) {
+        payOptions.setShowAlQRPaymentEvent(new Pair<>(s, orderId));
+    }
+
+    private void onAliPay(String s, String orderId, @NonNull PayOptions payOptions) {
+        AlipayInfo alipayInfo = new AlipayInfo();
+        alipayInfo.setContent(s);
+        requestSdkPay(alipayInfo, orderId, payOptions, Constants.PAYMENT_WAY_ALIPAY);
+    }
+
+    private void onWeChatScanPay(String s, String orderId, @NonNull PayOptions payOptions) {
+        payOptions.setShowWxQRPaymentEvent(new Pair<>(s, orderId));
+    }
+
+
+    private void onWeChatPay(String s, String orderId, @NonNull PayOptions payOptions) {
+        try {
+            WechatPaymentSignBean wechatPaymentSignBean = gson.fromJson(s, WechatPaymentSignBean.class);
+            WXpayInfo wXpayInfo = new WXpayInfo();
+            wXpayInfo.setAppid(wechatPaymentSignBean.getAppId());
+            wXpayInfo.setPartnerid(wechatPaymentSignBean.getPartnerId());
+            wXpayInfo.setPrepayid(wechatPaymentSignBean.getPrePayId());
+            wXpayInfo.set_package(wechatPaymentSignBean.getPackageName());
+            wXpayInfo.setNoncestr(wechatPaymentSignBean.getRandomStr());
+            wXpayInfo.setTimestamp(wechatPaymentSignBean.getTimeStamp());
+            wXpayInfo.setSign(wechatPaymentSignBean.getSign());
+            requestSdkPay(wXpayInfo, orderId, payOptions, Constants.PAYMENT_WAY_WECHAT);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void requestSdkPay(Object payInfo, String orderId, @NonNull PayOptions payOptions, int payWay) {
+        AgilePay.pay(payInfo, ActivityUtil.getTopActivity(), new AgilePayState() {
+            @Override
+            public void error(int errno, String error) {
+                if (errno == 6001) {
+                    //用户取消支付
+                    EventHelper.report(EventId.hf1000606, Maps.asMap(EventId.EVENT_ID, "hf11028"));
+                    return;
+                }
+                EventHelper.report(EventId.hf1000606, Maps.asMap(EventId.EVENT_ID, "hf11022"));
+                ToastUtil.show(R.string.member_payment_failed, ToastUtil.LENGTH_SHORT);
+            }
+
+            @Override
+            public void payError(int errno, String error) {
+                if (errno == 6001) {
+                    //用户取消支付
+                    EventHelper.report(EventId.hf1000606, Maps.asMap(EventId.EVENT_ID, "hf11028"));
+                    return;
+                }
+                EventHelper.report(EventId.hf1000606, Maps.asMap(EventId.EVENT_ID, "hf11022"));
+                ToastUtil.show(R.string.member_payment_failed, ToastUtil.LENGTH_SHORT);
+            }
+
+            @Override
+            public void paySuccess(String result) {
+                EventHelper.reportPay(payOptions.getAmount(), orderId, payWay, payOptions.getGoodsName());
+                payOptions.addDisposable(queryOrderStatus(orderId, payWay, payOptions, true, false));
+            }
+
+            @Override
+            public void payBefore() {
+
+            }
+        });
+    }
+
+    public @NonNull Disposable queryOrderStatus(String orderId, int payWay, @NonNull PayOptions payOptions, boolean showLoading, boolean isReportPay) {
+        if (isReportPay) {
+            OrderReportHelper.recordOrderId(payOptions.getAmount(), orderId, payWay, payOptions.getGoodsName());
+        }
+        return getPayStatus(orderId, 100, 3)
+                .doOnSubscribe(disposable -> {
+                    if (showLoading) {
+                        payOptions.setShowLoadingEvent(true);
+                        payOptions.addDisposable(RxJavaUtil.timer(10, TimeUnit.SECONDS, () -> payOptions.setShowLoadingEvent(false)));
+                    }
+                })
+                .doOnSuccess(success -> {
+                    if (isReportPay) {
+                        if (BoxingUtil.boxing(success)) {
+                            EventHelper.reportPay(payOptions.getAmount(), orderId, payWay, payOptions.getGoodsName());
+                        }
+                    }
+                })
+                .subscribe(aBoolean -> {
+                    payOptions.setShowLoadingEvent(false);
+                    if (BoxingUtil.boxing(aBoolean)) {
+                        EventHelper.report(EventId.hf1000604, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(payOptions.getMemberType())));
+                        Map<String, Object> map = new HashMap<>();
+                        if (Objects.equals(payOptions.getMemberType(), MemberType.APP_SUPER_RECOVER)) {
+                            map.put(EventId.EVENT_ID, "hf11026");
+                            EventHelper.report(EventId.hf1000605, map);
+                        } else if (Objects.equals(payOptions.getMemberType(), MemberType.APP_IMAGE_CLEAN) && payOptions.isPermanent()) {
+                            map.put(EventId.EVENT_ID, "hf11027");
+                            EventHelper.report(EventId.hf1000605, map);
+                        } else {
+                            map.put(EventId.EVENT_ID, "hf11025");
+                        }
+                        map.put(EventId.EVENT_TYPE1, ReportUtil.getReportId(payOptions.getMemberType()));
+                        EventHelper.report(EventId.hf1000605, map);
+                        payRepository.refreshOrderPageList();
+                        payOptions.setOnSubscribeSuccessEvent(orderId);
+                    }
+                }, throwable -> {
+                    throwable.printStackTrace();
+                    payOptions.setShowLoadingEvent(false);
+                });
     }
 
 
@@ -77,4 +262,5 @@ public class MemberRepository {
                 .doOnTerminate(deviceFuncRepository::refreshFuncAuths);
     }
 
+
 }

+ 9 - 6
app/src/main/java/com/datarecovery/master/dialog/AlipayQrCodeDialog.java

@@ -13,7 +13,6 @@ import com.atmob.common.ui.SizeUtil;
 import com.datarecovery.master.R;
 import com.datarecovery.master.data.consts.Constants;
 import com.datarecovery.master.databinding.DialogAlipayQrCodeBinding;
-import com.datarecovery.master.module.member.MemberViewModel;
 
 import atmob.reactivex.rxjava3.disposables.Disposable;
 
@@ -22,7 +21,7 @@ public class AlipayQrCodeDialog extends BaseDialog<DialogAlipayQrCodeBinding> {
     private static final String HTML_TEMPLATE = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/><title>Alipay QR Code</title></head><body>${qr_code_html}</body></html>";
     private @atmob.reactivex.rxjava3.annotations.NonNull Disposable queryOrderDisposable;
     private String orderId;
-    private MemberViewModel memberViewModel;
+    private OrderDisposable orderDisposable;
 
     @SuppressLint("SetJavaScriptEnabled")
     public AlipayQrCodeDialog(@NonNull Context context) {
@@ -50,15 +49,15 @@ public class AlipayQrCodeDialog extends BaseDialog<DialogAlipayQrCodeBinding> {
         super.onDismiss();
         if (queryOrderDisposable != null && !queryOrderDisposable.isDisposed()) {
             queryOrderDisposable.dispose();
-            memberViewModel.queryOrderStatus(orderId, Constants.PAYMENT_WAY_ALIPAY_SCAN, true, true);
+            orderDisposable.getOrderDisposable(orderId, Constants.PAYMENT_WAY_ALIPAY_SCAN, true, true);
         }
         binding.alipayQrCodeWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
     }
 
-    public void show(String qrCodeHtml, String orderId, MemberViewModel memberViewModel) {
-        this.memberViewModel = memberViewModel;
+    public void show(String qrCodeHtml, String orderId, OrderDisposable orderDisposable) {
+        this.orderDisposable = orderDisposable;
         this.orderId = orderId;
-        this.queryOrderDisposable = memberViewModel.queryOrderStatus(orderId, Constants.PAYMENT_WAY_ALIPAY_SCAN, false, true);
+        this.queryOrderDisposable = orderDisposable.getOrderDisposable(orderId, Constants.PAYMENT_WAY_ALIPAY_SCAN, false, true);
         binding.alipayQrCodeWebView.loadDataWithBaseURL(null, HTML_TEMPLATE.replace("${qr_code_html}", qrCodeHtml), "text/html", "utf-8", null);
         super.show();
     }
@@ -66,4 +65,8 @@ public class AlipayQrCodeDialog extends BaseDialog<DialogAlipayQrCodeBinding> {
     public String getCurrentOrderId() {
         return orderId;
     }
+
+    public interface OrderDisposable {
+        Disposable getOrderDisposable(String orderId, int payWay, boolean showLoading, boolean isReportPay);
+    }
 }

+ 9 - 6
app/src/main/java/com/datarecovery/master/dialog/WechatPayQrCodeDialog.java

@@ -9,7 +9,6 @@ import com.atmob.app.lib.base.BaseDialog;
 import com.datarecovery.master.R;
 import com.datarecovery.master.data.consts.Constants;
 import com.datarecovery.master.databinding.DialogWechatPayQrCodeBinding;
-import com.datarecovery.master.module.member.MemberViewModel;
 import com.datarecovery.master.utils.QrCodeUtil;
 
 import atmob.reactivex.rxjava3.disposables.Disposable;
@@ -21,7 +20,7 @@ public class WechatPayQrCodeDialog extends BaseDialog<DialogWechatPayQrCodeBindi
     private Disposable generateQRDisposable;
     private @atmob.reactivex.rxjava3.annotations.NonNull Disposable queryOrderDisposable;
     private String orderId;
-    private MemberViewModel memberViewModel;
+    private OrderDisposable orderDisposable;
 
     public WechatPayQrCodeDialog(@NonNull Context context) {
         super(context, R.style.Theme_Common_Dialog);
@@ -34,17 +33,17 @@ public class WechatPayQrCodeDialog extends BaseDialog<DialogWechatPayQrCodeBindi
         binding.wechatPayQrCode.setImageBitmap(null);
         if (queryOrderDisposable != null && !queryOrderDisposable.isDisposed()) {
             queryOrderDisposable.dispose();
-            memberViewModel.queryOrderStatus(orderId, Constants.PAYMENT_WAY_WECHAT_SCAN, true, true);
+            orderDisposable.getOrderDisposable(orderId, Constants.PAYMENT_WAY_WECHAT_SCAN, true, true);
         }
         if (generateQRDisposable != null && !generateQRDisposable.isDisposed()) {
             generateQRDisposable.dispose();
         }
     }
 
-    public void show(String qrCodeData, String orderId, MemberViewModel memberViewModel) {
-        this.memberViewModel = memberViewModel;
+    public void show(String qrCodeData, String orderId, OrderDisposable orderDisposable) {
         this.orderId = orderId;
-        this.queryOrderDisposable = memberViewModel.queryOrderStatus(orderId, Constants.PAYMENT_WAY_WECHAT_SCAN, false, true);
+        this.orderDisposable = orderDisposable;
+        this.queryOrderDisposable = orderDisposable.getOrderDisposable(orderId, Constants.PAYMENT_WAY_WECHAT_SCAN, false, true);
         if (generateQRDisposable != null && !generateQRDisposable.isDisposed()) {
             generateQRDisposable.dispose();
         }
@@ -58,4 +57,8 @@ public class WechatPayQrCodeDialog extends BaseDialog<DialogWechatPayQrCodeBindi
     public String getCurrentOrderId() {
         return orderId;
     }
+
+    public interface OrderDisposable {
+        Disposable getOrderDisposable(String orderId, int payWay, boolean showLoading, boolean isReportPay);
+    }
 }

+ 3 - 2
app/src/main/java/com/datarecovery/master/module/member/MemberActivity.java

@@ -36,6 +36,7 @@ import com.gyf.immersionbar.ImmersionBar;
 
 import java.util.Objects;
 
+import atmob.reactivex.rxjava3.disposables.Disposable;
 import dagger.hilt.android.AndroidEntryPoint;
 
 @AndroidEntryPoint
@@ -180,7 +181,7 @@ public class MemberActivity extends BaseActivity<ActivityMemberBinding> {
         if (wechatPayQrCodeDialog == null) {
             wechatPayQrCodeDialog = new WechatPayQrCodeDialog(this);
         }
-        wechatPayQrCodeDialog.show(params.first, params.second, memberViewModel);
+        wechatPayQrCodeDialog.show(params.first, params.second, (orderId, payWay, showLoading, isReportPay) -> memberViewModel.queryOrderStatus(orderId, payWay, showLoading, isReportPay));
     }
 
     private void showAlipayQrCodeDialog(Pair<String, String> params) {
@@ -190,7 +191,7 @@ public class MemberActivity extends BaseActivity<ActivityMemberBinding> {
         if (alipayQrCodeDialog == null) {
             alipayQrCodeDialog = new AlipayQrCodeDialog(this);
         }
-        alipayQrCodeDialog.show(params.first, params.second, memberViewModel);
+        alipayQrCodeDialog.show(params.first, params.second, (orderId, payWay, showLoading, isReportPay) -> memberViewModel.queryOrderStatus(orderId, payWay, showLoading, isReportPay));
     }
 
     private void dismissQrCodeDialog(String orderId) {

+ 1 - 0
app/src/main/java/com/datarecovery/master/module/member/MemberViewModel.java

@@ -353,6 +353,7 @@ public class MemberViewModel extends BaseViewModel {
             public void onSuccess(@NonNull MemberPayResponse memberPayResponse) {
                 showLoadingEvent.setValue(false);
                 orderIdItemMap.put(memberPayResponse.getOutTradeNo(), bean);
+                payRepository.refreshOrderPageList();
                 if (payPlatform == 1 && payMethod == 2) {
                     onWeChatPay(memberPayResponse.getWechatPayPrepayJson(), memberPayResponse.getOutTradeNo());
                 } else if (payPlatform == 4 && payMethod == 2) {

+ 76 - 1
app/src/main/java/com/datarecovery/master/module/order/OrderFragment.java

@@ -1,17 +1,25 @@
 package com.datarecovery.master.module.order;
 
 import android.os.Bundle;
+import android.util.Pair;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 
 import com.atmob.app.lib.base.BaseFragment;
+import com.datarecovery.master.data.api.bean.OrderBean;
+import com.datarecovery.master.data.api.bean.PayOptions;
 import com.datarecovery.master.databinding.FragmentOrderBinding;
+import com.datarecovery.master.dialog.AlipayQrCodeDialog;
+import com.datarecovery.master.dialog.CommonLoadingDialog;
+import com.datarecovery.master.dialog.WechatPayQrCodeDialog;
+import com.datarecovery.master.utils.BoxingUtil;
 import com.gyf.immersionbar.ImmersionBar;
 
+import java.util.Objects;
+
 import dagger.hilt.android.AndroidEntryPoint;
 
 @AndroidEntryPoint
@@ -21,6 +29,9 @@ public class OrderFragment extends BaseFragment<FragmentOrderBinding> {
     private OrderViewModel orderViewModel;
 
     private OrderItemAdapter orderItemAdapter;
+    private CommonLoadingDialog loadingDialog;
+    private AlipayQrCodeDialog alipayQrCodeDialog;
+    private WechatPayQrCodeDialog wechatPayQrCodeDialog;
 
 
     @Override
@@ -33,15 +44,79 @@ public class OrderFragment extends BaseFragment<FragmentOrderBinding> {
     private void initView() {
         orderItemAdapter = new OrderItemAdapter(getViewLifecycleOwner());
         binding.ryOrder.setAdapter(orderItemAdapter);
+        orderItemAdapter.setOrderActionHandler(new OrderItemAdapter.OrderActionHandler() {
+            @Override
+            public void cancel(OrderBean orderBean) {
+                orderViewModel.cancelOrder(orderBean);
+            }
+
+            @Override
+            public void payOrder(OrderBean orderBean) {
+                orderViewModel.payOrder(orderBean);
+            }
+        });
         binding.ryOrder.setLayoutManager(new LinearLayoutManager(getContext()));
         binding.swiperLayout.setOnRefreshListener(() -> orderViewModel.refreshOrderPageList());
     }
 
     private void initObserver() {
+        orderViewModel.getOnSubscribeSuccessEvent().observe(getViewLifecycleOwner(), this::dismissQrCodeDialog);
+        orderViewModel.getShowWxQRPaymentEvent().observe(getViewLifecycleOwner(), this::showWxQrCodeDialog);
+        orderViewModel.getShowAliQRPaymentEvent().observe(getViewLifecycleOwner(), this::showAlipayQrCodeDialog);
+        orderViewModel.getLoading().observe(getViewLifecycleOwner(), this::showLoadingDialog);
         orderViewModel.getOrderList().observe(getViewLifecycleOwner(), list -> orderItemAdapter.submit(list));
         orderViewModel.getRefreshOrderListEvent().observe(getViewLifecycleOwner(), o -> binding.swiperLayout.setRefreshing(false));
     }
 
+    private void showWxQrCodeDialog(PayOptions payOptions) {
+        if (payOptions == null || payOptions.getWxQrCode() == null) {
+            return;
+        }
+        Pair<String, String> params = payOptions.getWxQrCode();
+        if (wechatPayQrCodeDialog == null) {
+            wechatPayQrCodeDialog = new WechatPayQrCodeDialog(requireActivity());
+        }
+        wechatPayQrCodeDialog.show(params.first, params.second, (orderId, payWay, showLoading, isReportPay) -> orderViewModel.queryOrderStatus(orderId, payWay, payOptions, showLoading, isReportPay));
+    }
+
+    private void dismissQrCodeDialog(String orderId) {
+        if (alipayQrCodeDialog != null) {
+            if (Objects.equals(orderId, alipayQrCodeDialog.getCurrentOrderId())) {
+                alipayQrCodeDialog.dismiss();
+            }
+        }
+        if (wechatPayQrCodeDialog != null) {
+            if (Objects.equals(orderId, wechatPayQrCodeDialog.getCurrentOrderId())) {
+                wechatPayQrCodeDialog.dismiss();
+            }
+        }
+    }
+
+    private void showAlipayQrCodeDialog(PayOptions payOptions) {
+        if (payOptions == null || payOptions.getAlQrCode() == null) {
+            return;
+        }
+        Pair<String, String> params = payOptions.getAlQrCode();
+        if (alipayQrCodeDialog == null) {
+            alipayQrCodeDialog = new AlipayQrCodeDialog(requireActivity());
+        }
+        alipayQrCodeDialog.show(params.first, params.second, (orderId, payWay, showLoading, isReportPay) -> orderViewModel.queryOrderStatus(orderId, payWay, payOptions, showLoading, isReportPay));
+    }
+
+    public void showLoadingDialog(Boolean show) {
+        if (BoxingUtil.boxing(show)) {
+            if (loadingDialog == null) {
+                loadingDialog = new CommonLoadingDialog(requireActivity());
+                loadingDialog.setMessageGone();
+            }
+            loadingDialog.show();
+        } else {
+            if (loadingDialog != null) {
+                loadingDialog.dismiss();
+            }
+        }
+    }
+
     @Override
     protected void initViewModel() {
         super.initViewModel();

+ 12 - 0
app/src/main/java/com/datarecovery/master/module/order/OrderItemAdapter.java

@@ -55,6 +55,18 @@ public class OrderItemAdapter extends RecyclerView.Adapter<OrderItemAdapter.View
                 if (!Objects.equals(oldItem.getTutorialLink(), newItem.getTutorialLink())) {
                     return false;
                 }
+                if (!Objects.equals(oldItem.getStatus(), newItem.getStatus())) {
+                    return false;
+                }
+                if (!Objects.equals(oldItem.getExpirationDesc(), newItem.getExpirationDesc())) {
+                    return false;
+                }
+                if (!Objects.equals(oldItem.getOrderStatusDesc(), newItem.getOrderStatusDesc())) {
+                    return false;
+                }
+                if (!Objects.equals(oldItem.getCreateTime(), newItem.getCreateTime())) {
+                    return false;
+                }
                 return true;
             }
         });

+ 99 - 1
app/src/main/java/com/datarecovery/master/module/order/OrderViewModel.java

@@ -1,13 +1,21 @@
 package com.datarecovery.master.module.order;
 
+import android.accounts.NetworkErrorException;
+
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 
 import com.atmob.app.lib.base.BaseViewModel;
+import com.atmob.app.lib.handler.RxHttpHandler;
 import com.atmob.app.lib.livedata.SingleLiveEvent;
+import com.datarecovery.master.R;
 import com.datarecovery.master.data.api.bean.OrderBean;
+import com.datarecovery.master.data.api.bean.PayOptions;
 import com.datarecovery.master.data.api.response.OrderPageResponse;
+import com.datarecovery.master.data.consts.ErrorCode;
+import com.datarecovery.master.data.repositories.MemberRepository;
 import com.datarecovery.master.data.repositories.PayRepository;
+import com.datarecovery.master.utils.ToastUtil;
 
 import java.util.List;
 
@@ -25,15 +33,38 @@ public class OrderViewModel extends BaseViewModel {
 
     private final PayRepository payRepository;
 
+    private final MutableLiveData<Boolean> loading = new MutableLiveData<>();
 
     private final SingleLiveEvent<?> refreshOrderListEvent = new SingleLiveEvent<>();
+    private final SingleLiveEvent<PayOptions> showWxQRPaymentEvent = new SingleLiveEvent<>();
+    private final SingleLiveEvent<PayOptions> showAliQRPaymentEvent = new SingleLiveEvent<>();
+    private final SingleLiveEvent<String> onSubscribeSuccessEvent = new SingleLiveEvent<>();
+    private final MemberRepository memberRepository;
+
 
     @Inject
-    public OrderViewModel(PayRepository payRepository) {
+    public OrderViewModel(PayRepository payRepository, MemberRepository memberRepository) {
         this.payRepository = payRepository;
+        this.memberRepository = memberRepository;
         refreshOrderPageList();
     }
 
+    public LiveData<String> getOnSubscribeSuccessEvent() {
+        return onSubscribeSuccessEvent;
+    }
+
+    public LiveData<PayOptions> getShowWxQRPaymentEvent() {
+        return showWxQRPaymentEvent;
+    }
+
+    public LiveData<PayOptions> getShowAliQRPaymentEvent() {
+        return showAliQRPaymentEvent;
+    }
+
+    public LiveData<Boolean> getLoading() {
+        return loading;
+    }
+
     public LiveData<List<OrderBean>> getOrderList() {
         return payRepository.getOrderList();
     }
@@ -60,4 +91,71 @@ public class OrderViewModel extends BaseViewModel {
             }
         });
     }
+
+
+    public void cancelOrder(OrderBean orderBean) {
+        if (orderBean == null) {
+            return;
+        }
+        payRepository.cancelOrder(orderBean.getId()).subscribe(new SingleObserver<Object>() {
+            @Override
+            public void onSubscribe(@NonNull Disposable d) {
+                addDisposable(d);
+                loading.setValue(true);
+            }
+
+            @Override
+            public void onSuccess(@NonNull Object o) {
+                loading.setValue(false);
+                refreshOrderPageList();
+                ToastUtil.show(R.string.order_cancel_order_success, ToastUtil.LENGTH_SHORT);
+
+            }
+
+            @Override
+            public void onError(@NonNull Throwable e) {
+                loading.setValue(false);
+                if (e instanceof NetworkErrorException) {
+                    ToastUtil.show(R.string.net_error, ToastUtil.LENGTH_SHORT);
+                    return;
+                }
+                RxHttpHandler.ServerErrorException serverErrorException
+                        = e instanceof RxHttpHandler.ServerErrorException ? ((RxHttpHandler.ServerErrorException) e) : null;
+                if (serverErrorException != null) {
+                    if (serverErrorException.getCode() == ErrorCode.ERROR_CODE_CANCEL_ORDER_ERROR) {
+                        return;
+                    }
+                }
+                ToastUtil.show(e.getMessage(), ToastUtil.LENGTH_SHORT);
+            }
+        });
+    }
+
+    public void payOrder(OrderBean orderBean) {
+        if (orderBean == null) {
+            return;
+        }
+        int payPlatform = orderBean.getPayPlatform();
+        int payMethod = orderBean.getPayMethod();
+        int goodsId = orderBean.getItemId();
+        memberRepository.payOrder(new PayOptions(
+                this::addDisposable,
+                goodsId,
+                orderBean.getItemName(),
+                orderBean.getFenAmount(),
+                payPlatform,
+                payMethod,
+                orderBean.isPermanent(),
+                orderBean.getAuths(),
+                loading::setValue,
+                onSubscribeSuccessEvent::setValue,
+                showWxQRPaymentEvent::setValue,
+                showAliQRPaymentEvent::setValue
+        ));
+    }
+
+
+    public Disposable queryOrderStatus(String orderId, int payWay, PayOptions payOptions, boolean showLoading, boolean isReportPay) {
+        return memberRepository.queryOrderStatus(orderId, payWay, payOptions, showLoading, isReportPay);
+    }
 }

+ 2 - 2
app/src/main/res/drawable/bg_order_copy.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#E4EFFF" />
-    <corners android:radius="2dp" />
+    <solid android:color="#EFEFEF" />
+    <corners android:radius="12dp" />
 </shape>

+ 61 - 29
app/src/main/res/layout/item_order.xml

@@ -32,6 +32,7 @@
 
         <import type="com.datarecovery.master.utils.DateUtil" />
 
+        <import type="com.datarecovery.master.data.api.bean.OrderBean.OrderStatus" />
     </data>
 
     <androidx.constraintlayout.widget.ConstraintLayout
@@ -60,34 +61,19 @@
             app:layout_constraintTop_toTopOf="parent" />
 
         <TextView
-            android:id="@+id/tv_order"
+            android:id="@+id/tv_order_status"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="12dp"
-            android:text="@{orderBean.outTradeNo}"
+            android:text="@{orderBean.orderStatusDesc}"
             android:textColor="#202020"
             android:textSize="14sp"
             android:textStyle="bold"
             app:layout_constraintBottom_toBottomOf="@+id/order_header"
             app:layout_constraintStart_toStartOf="@+id/order_header"
             app:layout_constraintTop_toTopOf="@+id/order_header"
-            tools:text="订单号:adfe21649518788" />
+            tools:text="订单已完成" />
 
-        <TextView
-            android:id="@+id/tv_copy_order"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="12dp"
-            android:background="@drawable/bg_order_copy"
-            android:onClick="@{copyOrderClick}"
-            android:paddingHorizontal="10dp"
-            android:paddingVertical="2dp"
-            android:text="@string/order_copy"
-            android:textColor="@color/colorPrimary"
-            android:textSize="12sp"
-            app:layout_constraintBottom_toBottomOf="@+id/order_header"
-            app:layout_constraintEnd_toEndOf="@+id/order_header"
-            app:layout_constraintTop_toTopOf="@+id/order_header" />
 
         <Space
             android:id="@+id/space1"
@@ -102,7 +88,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/order_buy_type"
-            app:layout_constraintLeft_toLeftOf="@+id/tv_order"
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
             app:layout_constraintTop_toBottomOf="@+id/space1" />
 
         <TextView
@@ -125,14 +111,57 @@
 
 
         <TextView
+            android:id="@+id/tv_order_number"
+            style="@style/Order_List_Content_Txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/order_number"
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
+            app:layout_constraintTop_toBottomOf="@+id/space2" />
+
+        <TextView
+            android:id="@+id/tv_outTrade_No"
+            style="@style/Order_List_Content_Txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:maxWidth="150dp"
+            android:singleLine="true"
+            android:text="@{orderBean.outTradeNo}"
+            app:layout_constraintBaseline_toBaselineOf="@+id/tv_order_number"
+            app:layout_constraintStart_toEndOf="@+id/tv_order_number"
+            tools:text="adjf589489165561" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6dp"
+            android:background="@drawable/bg_order_copy"
+            android:onClick="@{copyOrderClick}"
+            android:paddingHorizontal="8dp"
+            android:paddingVertical="1dp"
+            android:text="@string/order_copy"
+            android:textColor="#404040"
+            android:textSize="12sp"
+            app:layout_constraintBaseline_toBaselineOf="@+id/tv_outTrade_No"
+            app:layout_constraintStart_toEndOf="@+id/tv_outTrade_No" />
+
+        <Space
+            android:id="@+id/space_number"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintDimensionRatio="360:8"
+            app:layout_constraintTop_toBottomOf="@+id/tv_order_number" />
+
+        <TextView
             android:id="@+id/tv_auth_code_title"
             style="@style/Order_List_Content_Txt"
             isGone="@{!orderBean.isShowLink}"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/order_auth_code"
-            app:layout_constraintLeft_toLeftOf="@+id/tv_order"
-            app:layout_constraintTop_toBottomOf="@+id/space2" />
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
+            app:layout_constraintTop_toBottomOf="@+id/space_number" />
 
 
         <TextView
@@ -181,7 +210,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/order_buy_time"
-            app:layout_constraintLeft_toLeftOf="@+id/tv_order"
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
             app:layout_constraintTop_toBottomOf="@+id/space_auth_code" />
 
         <TextView
@@ -189,7 +218,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
-            android:text="@{DateUtil.formatNormalDate(DateUtil.YYYY_MM_DD_HH_MM_SS,orderBean.startTimestamp)}"
+            android:text="@{orderBean.createTime}"
             app:layout_constraintBaseline_toBaselineOf="@+id/tv_order_buy_time"
             app:layout_constraintStart_toEndOf="@+id/tv_order_buy_time"
             tools:text="2023-12-16" />
@@ -209,7 +238,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/order_member_validity"
-            app:layout_constraintLeft_toLeftOf="@+id/tv_order"
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
             app:layout_constraintTop_toBottomOf="@+id/space3" />
 
         <TextView
@@ -217,7 +246,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
-            android:text="@{orderBean.expireDate}"
+            android:text="@{orderBean.expirationDesc}"
             app:layout_constraintBaseline_toBaselineOf="@+id/tv_order_member_validity"
             app:layout_constraintStart_toEndOf="@+id/tv_order_member_validity"
             tools:text="永久有效" />
@@ -234,9 +263,10 @@
             android:id="@+id/v_line"
             android:layout_width="0dp"
             android:layout_height="1dp"
+            android:layout_marginEnd="12dp"
             android:background="#F5F5F5"
-            app:layout_constraintEnd_toEndOf="@+id/tv_copy_order"
-            app:layout_constraintStart_toStartOf="@+id/tv_order"
+            app:layout_constraintEnd_toEndOf="@+id/order_header"
+            app:layout_constraintStart_toStartOf="@+id/tv_order_status"
             app:layout_constraintTop_toBottomOf="@+id/space4" />
 
         <Space
@@ -253,12 +283,13 @@
             android:textColor="#404040"
             android:textSize="14sp"
             app:layout_constraintBottom_toTopOf="@+id/space7"
-            app:layout_constraintLeft_toLeftOf="@+id/tv_order"
+            app:layout_constraintLeft_toLeftOf="@+id/tv_order_status"
             app:layout_constraintTop_toBottomOf="@+id/space5"
             tools:text="金额:¥66" />
 
         <TextView
             android:id="@+id/tv_cancel_order"
+            isGone="@{orderBean.status != OrderStatus.TODO}"
             android:layout_width="0dp"
             android:layout_height="0dp"
             android:layout_marginEnd="12dp"
@@ -276,13 +307,14 @@
             app:layout_constraintWidth_percent="0.2444444444444444" />
 
         <TextView
-            android:onClick="@{payOrderClick}"
             android:id="@+id/tv_pay"
+            isGone="@{orderBean.status != OrderStatus.TODO}"
             android:layout_width="0dp"
             android:layout_height="0dp"
             android:layout_marginEnd="12dp"
             android:background="@drawable/bg_common_btn"
             android:gravity="center"
+            android:onClick="@{payOrderClick}"
             android:text="@string/order_pay"
             android:textColor="@color/white"
             android:textSize="14sp"

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -204,4 +204,7 @@
     <string name="order_cancel_order">取消订单</string>
     <string name="order_pay">去支付</string>
     <string name="order_cancel_order_success">取消成功</string>
+    <string name="order_success">订单已完成</string>
+    <string name="order_todo_pay">订单待支付</string>
+    <string name="order_number">订单号</string>
 </resources>