From c1da062d1da2aba997d7b24561b3c787969a5190 Mon Sep 17 00:00:00 2001 From: terrylee Date: Fri, 26 Jun 2026 14:52:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(sale):=20=E9=80=B2=E4=BB=98=E6=AC=BE?= =?UTF-8?q?=E5=89=8D=E7=A2=BA=E8=AA=8D=E4=B8=8B=E4=BD=8D=E6=A9=9F(XinYuan)?= =?UTF-8?q?=E9=80=A3=E7=B7=9A=EF=BC=8C=E5=A4=B1=E9=80=A3=E8=87=AA=E5=8B=95?= =?UTF-8?q?=E9=87=8D=E9=80=A3=20(=E7=A7=BB=E6=A4=8D=20Honlifeng)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 點商品進付款(onBuyRequested)前先 ping 主櫃下位機;失連→checkAndReconnectDev 重連→再確認, 連上才開 BuyDialog、連不上跳大字提示擋住。檢查期間顯示「下位機連線判斷中」轉圈框。 MyApp 新增 pingXinYuanOnce/isXinYuanAlive/ensureXinYuanConnected。 Co-Authored-By: Claude Opus 4.8 --- .../java/com/unibuy/smartdevice/MyApp.java | 34 +++++++++ .../unibuy/smartdevice/ui/BasicSaleFlow.java | 76 ++++++++++++++++++- 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/unibuy/smartdevice/MyApp.java b/app/src/main/java/com/unibuy/smartdevice/MyApp.java index 7c2dc55..14d2e8f 100644 --- a/app/src/main/java/com/unibuy/smartdevice/MyApp.java +++ b/app/src/main/java/com/unibuy/smartdevice/MyApp.java @@ -643,6 +643,40 @@ public class MyApp extends Application { return true; } + /** + * 進支付前用:送同步包確認主櫃(XinYuan)下位機是否真的有回應。 + * 先清掉舊 buffer 再送,避免讀到先前殘留的回應誤判為已連線。 + * 注意:會 Thread.sleep,請在背景執行緒呼叫。 + */ + private static boolean pingXinYuanOnce(long waitMs) { + if (devXinYuanController == null) return false; + try { + devXinYuanController.clearReadBuffers(); + devXinYuanController.addSendBuffer( + devXinYuanController.getDevXinYuan().syncPackNo((byte) 0x01)); + Thread.sleep(waitMs); + return !devXinYuanController.getReadBufferByMax().isEmpty(); + } catch (Exception e) { + return false; + } + } + + /** 連送同步包兩次,任一次有回應即視為連線,降低偶發誤判(單次沒回不代表失連)。 */ + public static boolean isXinYuanAlive() { + return pingXinYuanOnce(300) || pingXinYuanOnce(300); + } + + /** + * 進支付前用:確認主櫃下位機連線;若失連則主動重連後再確認。 + * 回傳最終是否連線成功(false=連不上,呼叫端應擋住不進支付)。 + * 注意:會 Thread.sleep,請在背景執行緒呼叫。 + */ + public static boolean ensureXinYuanConnected() { + if (isXinYuanAlive()) return true; + checkAndReconnectDev(); // 主動重連 + return isXinYuanAlive(); // 重連後再確認 + } + public void initMqtt() { String machineId = MyApp.getInstance().getMachine().getMachineID(); String apiKey = MyApp.getInstance().getMachine().getApiKey(); diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/BasicSaleFlow.java b/app/src/main/java/com/unibuy/smartdevice/ui/BasicSaleFlow.java index 5c2f377..6279780 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/BasicSaleFlow.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/BasicSaleFlow.java @@ -28,7 +28,81 @@ public class BasicSaleFlow extends SaleFlowHandler { @Override public void onBuyRequested() { - new BuyDialog(context, handlerMain).show(); + // 進支付前先確認主櫃下位機(XinYuan)連線: + // 僅當購物清單含主櫃(VMC)商品才檢查;失連→主動重連→仍連不上則擋住不進支付。 + if (!buyListHasMainCabinetItem()) { + new BuyDialog(context, handlerMain).show(); + return; + } + // 顯示「連線判斷中」轉圈提示,避免客戶以為當機。 + final android.app.AlertDialog progress = buildConnectingDialog(); + try { progress.show(); } catch (Exception ignore) {} + // ensureXinYuanConnected 內含 Thread.sleep,必須在背景執行緒跑。 + new Thread(() -> { + boolean connected = MyApp.ensureXinYuanConnected(); + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + try { if (progress.isShowing()) progress.dismiss(); } catch (Exception ignore) {} + if (!connected) { + showNotConnectedDialog(); + return; + } + new BuyDialog(context, handlerMain).show(); + }); + }).start(); + } + + /** 購物清單是否含主櫃(VMC)商品;非主櫃商品不需檢查主櫃下位機連線。 */ + private boolean buyListHasMainCabinetItem() { + for (BuyStructure b : MyApp.getInstance().getBuyList()) { + if (b.getField() == com.unibuy.smartdevice.devices.SlotField.VMC.getField()) { + return true; + } + } + return false; + } + + /** 連線判斷中:轉圈圈 + 提示文字(不可取消)。 */ + private android.app.AlertDialog buildConnectingDialog() { + android.widget.LinearLayout layout = new android.widget.LinearLayout(context); + layout.setOrientation(android.widget.LinearLayout.HORIZONTAL); + layout.setGravity(android.view.Gravity.CENTER_VERTICAL); + int pad = dp(32); + layout.setPadding(pad, pad, pad, pad); + + android.widget.ProgressBar spinner = new android.widget.ProgressBar(context); + android.widget.LinearLayout.LayoutParams sp = + new android.widget.LinearLayout.LayoutParams(dp(56), dp(56)); + sp.rightMargin = dp(24); + layout.addView(spinner, sp); + + android.widget.TextView tv = new android.widget.TextView(context); + tv.setText("下位機連線判斷中......"); + tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28); + tv.setTextColor(android.graphics.Color.BLACK); + layout.addView(tv); + + return new android.app.AlertDialog.Builder(context) + .setView(layout) + .setCancelable(false) + .create(); + } + + /** 主櫃未連線:跳出大字提示框。 */ + private void showNotConnectedDialog() { + android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context) + .setMessage("主櫃尚未連線,請稍後再試") + .setCancelable(false) + .setPositiveButton("確定", (d, w) -> d.dismiss()) + .create(); + try { dialog.show(); } catch (Exception ignore) { return; } + android.widget.TextView msg = dialog.findViewById(android.R.id.message); + if (msg != null) msg.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28); + android.widget.Button btn = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE); + if (btn != null) btn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 24); + } + + private int dp(int v) { + return Math.round(v * context.getResources().getDisplayMetrics().density); } /**