[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:
parent
4afc3a59d4
commit
42184c7077
@ -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;
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user