[FEAT] 購物車模式進入銷售頁時新增取貨口總閘門

1. RecyclerVmcBuyListAdpter 新增獨立的 checkPickupPortOnEntry() 與 DevEntryGateByVMC 讀取器,於進入銷售頁時檢查取貨口狀態(0x05 有東西 / 0x06、0x09 門未關),不影響員工卡點擊用的 checkSlotHaveProduct,維持線上 main 流程逐字等價。
2. 取貨口異常時跳提示(放大字級),使用者確認或 10 秒後自動執行 onBlocked 回待機;機器無回應 3000ms 超時則放行購物(fail-open,與既有行為一致)。
3. VmcFragment.startOnSwitchFragment 觸發 maybeRunPickupPortEntryGate,僅在 shopping_mode==basic 且購物車開啟時啟用,員工卡流程不重複檢查。
4. 以 entryGateChecked 旗標確保每輪購物只檢查一次(切購物車頁再切回不重跳);延遲 2500ms 送出等下位機連線就緒;被擋下時呼叫 FontendActivity.ReSetAPP() 回待機,使三個加購入口(加入購物車/直接購買/快速加購)在源頭一併被擋。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sky121113 2026-06-16 17:31:08 +08:00
parent 4afc3a59d4
commit 42184c7077
2 changed files with 127 additions and 1 deletions

View File

@ -357,6 +357,101 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
}
}
// ===== 購物車模式專用進入銷售頁時的取貨口總閘門 =====
// 與員工卡點擊用的 checkSlotHaveProduct / DevCheckSlotByVMC 完全獨立不影響線上 main(晟崴/中國醫)流程
// 取貨口有東西(0x05) 門沒關(0x06/0x09) 時跳提示使用者確認後執行 onBlocked回待機
// 機器無回應(3000ms 超時) 視為正常放行避免卡死
public void checkPickupPortOnEntry(int slot, Runnable onBlocked) {
java.util.concurrent.atomic.AtomicBoolean isHandled = new java.util.concurrent.atomic.AtomicBoolean(false);
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (!isHandled.getAndSet(true)) {
logs.info("Entry Gate Check Timeout - 放行購物 (Timeout Safeguard)");
}
}, 3000);
MyApp.getInstance().getDevXinYuanController().addSendBuffer(
MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlotPre(slot, 1));
MyApp.getInstance().getDevXinYuanController().addSendBuffer(
MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot));
MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(
new DevEntryGateByVMC(fragment.getSrcHandlerMain(), isHandled, onBlocked), 800);
}
public class DevEntryGateByVMC extends DevController.ReadBufferOnScheduler {
private final java.util.concurrent.atomic.AtomicBoolean isHandled;
private final Runnable onBlocked;
public DevEntryGateByVMC(HandlerMain handlerMain,
java.util.concurrent.atomic.AtomicBoolean isHandled, Runnable onBlocked) {
super(handlerMain);
this.isHandled = isHandled;
this.onBlocked = onBlocked;
}
@Override
protected HandlerMain setHandlerMain() {
return getSrcHandlerMain();
}
@Override
protected Class<?> setCls() {
return getClass();
}
@Override
public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) {
boolean haveThings = false;
boolean doorError = false;
for (byte[] buffer : devController.getReadBufferByMax()) {
if (buffer[0] == 0x02) {
if (buffer[1] == 0x05) {
haveThings = true;
break;
} else if (buffer[1] == 0x06 || buffer[1] == 0x09) {
doorError = true;
break;
}
}
}
// 狀態安全不做任何事放行購物總閘門通過
if (!haveThings && !doorError) return;
// 已被超時或前一筆回覆處理過避免重複跳窗
if (isHandled.getAndSet(true)) return;
final String message = doorError
? "取貨口門未正常關閉,請先確認門已關好。"
: context.getString(R.string.there_is_something_at_the_pickup_port);
new Handler(Looper.getMainLooper()).post(() -> {
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.abnormal_reminder)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(R.string.i_knew, (d, which) -> {
d.dismiss();
if (onBlocked != null) onBlocked.run();
})
.create();
dialog.show();
TextView messageView = dialog.findViewById(android.R.id.message);
if (messageView != null) {
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
}
// 10 秒後自動回待機避免無人理會卡在提示
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (dialog.isShowing()) {
dialog.dismiss();
if (onBlocked != null) onBlocked.run();
}
}, 10_000);
});
}
}
public class DevDetectSlotByVMC extends DevController.ReadBufferOnScheduler {
private int position;

View File

@ -313,8 +313,39 @@ public class VmcFragment extends FragmentAbstract {
|| product.getSellingPrice() < 0;
}
private boolean entryGateChecked = false;
@Override
public void startOnSwitchFragment() { updateFlowerBuyList(); }
public void startOnSwitchFragment() {
updateFlowerBuyList();
maybeRunPickupPortEntryGate();
}
/**
* 購物車模式shopping_mode==basic 且購物車開啟進入銷售頁時的取貨口總閘門
* 取貨口有東西或門沒關就跳提示並回待機確保加入購物車 / 直接購買 / 快速加購
* 三條路徑在源頭被擋住每輪購物只檢查一次FontendActivity 每輪重建 fragment旗標自動重置
* 員工卡(employee_card)流程已在點擊時自行檢查不在此重複
*/
private void maybeRunPickupPortEntryGate() {
if (entryGateChecked) return;
boolean isBasic = "basic".equals(MyApp.getInstance().getShoppingMode());
boolean cartOn = MyApp.getInstance().getDevSet() != null
&& MyApp.getInstance().getDevSet().isShoppingCar();
if (!isBasic || !cartOn) return;
if (recyclerVmcBuyListAdpter == null) return;
entryGateChecked = true;
// 延遲送出等下位機(新源 VMC)連線就緒過早送出會超時放行而失去保護
new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
int gateSlot = (slotList != null && !slotList.isEmpty()) ? slotList.get(0).getSlot() : 1;
recyclerVmcBuyListAdpter.checkPickupPortOnEntry(gateSlot, () -> {
if (getActivity() instanceof FontendActivity) {
((FontendActivity) getActivity()).ReSetAPP();
}
});
}, 2500);
}
@Override
public void stopOnSwitchFragment() {