[FIX] 攔截硬體 Enter 鍵防止誤觸並修正遠端出貨交易紀錄
1. 在 FontendActivity 中攔截購物畫面非預期的 Enter 鍵事件,防止讀卡機誤觸員工卡對話框。 2. 關閉 VMC 與 Electric 商品列表圖片的 focusable 屬性,杜絕硬體 Enter 鍵觸發點擊事件。 3. 修正 MqttService 中的遠端出貨邏輯,改用 TransactionFinalizeBuilder 走完整交易結案流程。 4. 確保遠端出貨時正確填充 BuyList 與 ReportFlowInfoData,使後台能產生完整的訂單紀錄。 5. 在 NFC 支付對話框取消時,確保呼叫 nfcPayController.shutdown() 釋放硬體資源。
This commit is contained in:
parent
e5feac1608
commit
f5619e754e
@ -119,6 +119,8 @@ public class MyApp extends Application {
|
||||
public ReportDispatchInfoStructure reportDispatchInfoData;
|
||||
public CreditCardReceiptStructure creditCardReceiptInfoData;
|
||||
public boolean isShowDialog;
|
||||
public volatile boolean isNfcPaymentActive; // 用於控制 NFC 支付感應器的活動門禁
|
||||
|
||||
public boolean isDetectHost;
|
||||
public String suAccess;
|
||||
|
||||
@ -830,5 +832,14 @@ public class MyApp extends Application {
|
||||
isDetectHost = detectHost;
|
||||
}
|
||||
|
||||
public boolean isNfcPaymentActive() {
|
||||
return isNfcPaymentActive;
|
||||
}
|
||||
|
||||
public void setNfcPaymentActive(boolean nfcPaymentActive) {
|
||||
isNfcPaymentActive = nfcPaymentActive;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -57,6 +57,7 @@ public class DevNFCPayController extends HandlerMainScheduler {
|
||||
this.payType = payType;
|
||||
this.totalPrice = totalPrice;
|
||||
clearOldBuffer();
|
||||
MyApp.getInstance().setNfcPaymentActive(true);
|
||||
byte[] sendBuffer = devNFCPay.swipe(payType, totalPrice);
|
||||
String sendData = new String(sendBuffer);
|
||||
getLogs().info("電文送出:" + sendData);
|
||||
@ -113,19 +114,22 @@ public class DevNFCPayController extends HandlerMainScheduler {
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
isShuttingDown = true; // ✅ 標記關閉
|
||||
isShuttingDown = true; // ✅ 標記正在關閉,避免再次回報錯誤
|
||||
MyApp.getInstance().setNfcPaymentActive(false); // ✅ 關閉 NFC 支付感應
|
||||
super.shutdown();
|
||||
close(getHandlerMain());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
getLogs().info("⚠️ App 不在對話框狀態,忽略 NFC 感應訊號並停止服務");
|
||||
if (!MyApp.getInstance().isShowDialog() || !MyApp.getInstance().isNfcPaymentActive()) {
|
||||
getLogs().info("⚠️ NFC 支付狀態未啟動或對話框已關閉,停止服務");
|
||||
shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
byte[] readBuffer = comPort2.readToMatch(new byte[]{0x03}, 4096);
|
||||
getLogs().info("readBuffer length:" + readBuffer.length);
|
||||
|
||||
@ -17,8 +17,15 @@ import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.unibuy.smartdevice.HomeActivity;
|
||||
import com.unibuy.smartdevice.R;
|
||||
import com.unibuy.smartdevice.MyApp;
|
||||
import com.unibuy.smartdevice.devices.SlotField;
|
||||
import com.unibuy.smartdevice.exception.LogsEmptyException;
|
||||
import com.unibuy.smartdevice.external.mqtt.MqttManager;
|
||||
import com.unibuy.smartdevice.structure.BuyStructure;
|
||||
import com.unibuy.smartdevice.structure.ReportFlowInfoStructure;
|
||||
import com.unibuy.smartdevice.structure.SlotStructure;
|
||||
import com.unibuy.smartdevice.structure.mqtt.TransactionFinalizePayload;
|
||||
import com.unibuy.smartdevice.tools.TransactionFinalizeBuilder;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
@ -225,51 +232,56 @@ public class MqttService extends Service {
|
||||
Log.i(TAG, "Remote dispense success for slot: " + finalSlot + " (cmdId: " + cmdId + ")");
|
||||
mqttManager.publishCommandAck(cmdId, "success", "Product dispensed from slot: " + finalSlot);
|
||||
|
||||
// 觸發交易結案流程 (使庫存扣除與訂單紀錄正式化)
|
||||
// [修正] 使用 TransactionFinalizeBuilder 走完整交易流程,確保後台有訂單紀錄
|
||||
try {
|
||||
Log.i(TAG, "Preparing to send Transaction Finalize for remote dispense...");
|
||||
String flowId = com.unibuy.smartdevice.tools.FlowIdGenerator.next(getApplicationContext());
|
||||
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.TAIWAN).format(new Date());
|
||||
|
||||
TransactionFinalizePayload finalize = new TransactionFinalizePayload();
|
||||
finalize.setFlow_id(flowId);
|
||||
finalize.setPayment_type(100); // 遠端管理出貨
|
||||
|
||||
// 嘗試解析 cmdId 為數字。若為非數字(例如 UUID),則填 0
|
||||
int numericCmdId = 0;
|
||||
try {
|
||||
if (cmdId != null) numericCmdId = Integer.parseInt(cmdId);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "cmdId is not a number: " + cmdId + ", using 0 for code_id");
|
||||
// Step 1: 從記憶體中找到對應貨道的商品資料
|
||||
SlotStructure slotStructure = null;
|
||||
for (SlotField slotField : SlotField.values()) {
|
||||
try {
|
||||
slotStructure = MyApp.getInstance().getSlotData(slotField.getField(), finalSlot);
|
||||
break; // 找到就停止
|
||||
} catch (LogsEmptyException ignored) {
|
||||
// 此 field 找不到,繼續找下一個
|
||||
}
|
||||
}
|
||||
|
||||
finalize.setCode_id(numericCmdId);
|
||||
finalize.setRemote_command_id(numericCmdId);
|
||||
|
||||
TransactionFinalizePayload.OrderPayload order = new TransactionFinalizePayload.OrderPayload();
|
||||
order.setOrder_no(flowId);
|
||||
order.setPayment_type(100);
|
||||
order.setPayment_status(1);
|
||||
order.setMachine_time(time);
|
||||
order.setTotal_amount(0);
|
||||
order.setPay_amount(0);
|
||||
finalize.setOrder(order);
|
||||
if (slotStructure == null) {
|
||||
Log.e(TAG, "Remote dispense finalize failed: slot " + finalSlot + " not found in memory");
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionFinalizePayload.DispenseRecord disp = new TransactionFinalizePayload.DispenseRecord(
|
||||
String.valueOf(finalSlot),
|
||||
0, // 此處不帶商品 ID,由後端根據貨道對應
|
||||
0,
|
||||
0,
|
||||
1, // 成功
|
||||
-1, // 庫存由後端遞減
|
||||
time
|
||||
// Step 2: 清空並重新設定 BuyList(模擬使用者點選此商品)
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
MyApp.getInstance().getBuyList().add(
|
||||
new BuyStructure(slotStructure.getField(), finalSlot, slotStructure.getProduct(), 1)
|
||||
);
|
||||
finalize.setDispense(Collections.singletonList(disp));
|
||||
|
||||
mqttManager.publishTransactionFinalize(finalize);
|
||||
Log.i(TAG, "Remote dispense transaction finalized sent (flow_id: " + flowId + ")");
|
||||
// Step 3: 設定 ReportFlowInfoData(payment_type=100 代表遠端管理出貨)
|
||||
ReportFlowInfoStructure flowInfo = MyApp.getInstance().getReportFlowInfoData();
|
||||
flowInfo.setFlowType(100); // 遠端管理出貨
|
||||
flowInfo.setTotalPrice(0); // 免費出貨
|
||||
flowInfo.setGiveChange(0);
|
||||
flowInfo.setUsePoints(0);
|
||||
flowInfo.setFlowBarCode("");
|
||||
flowInfo.setFlowSendInfo(""); // 無支付請求封包
|
||||
flowInfo.setFlowRequestInfo(""); // 無支付回應封包
|
||||
flowInfo.setInvoiceInfo("");
|
||||
|
||||
// Step 4: 用 Builder 組裝並送出 Finalize Payload
|
||||
TransactionFinalizeBuilder builder = new TransactionFinalizeBuilder();
|
||||
int remaining = slotStructure.getCount() - 1;
|
||||
int productIdInt = 0;
|
||||
try { productIdInt = Integer.parseInt(slotStructure.getProduct().getProductID()); } catch (Exception ignored) {}
|
||||
builder.addDispenseRecord(finalSlot, productIdInt, 0, 0, true, remaining);
|
||||
|
||||
MqttManager.getInstance().publishTransactionFinalize(builder.build());
|
||||
Log.i(TAG, "[Remote Dispense] Transaction finalize sent via TransactionFinalizeBuilder, slot=" + finalSlot);
|
||||
|
||||
// 清空 BuyList 避免影響後續正常交易
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to construct/send finalize for remote dispense: " + e.getMessage(), e);
|
||||
Log.e(TAG, "Failed to send finalize for remote dispense: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1008,6 +1008,16 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
// [修正] 防止讀卡機 (HID) 在購物畫面送出 Enter 導致誤點擊商品
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
||||
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
getLogs().info("攔截到購物畫面非預期的 Enter 鍵,已忽略防止誤觸");
|
||||
}
|
||||
return true; // 消耗事件
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN && event.isLongPress()) {
|
||||
Toast.makeText(this, getString(R.string.back_home), Toast.LENGTH_SHORT).show();
|
||||
|
||||
@ -101,6 +101,8 @@ public class RecyclerElectricBuyListAdpter extends RecyclerView.Adapter<Recycler
|
||||
}
|
||||
|
||||
if (count > 0 && !isLock) {
|
||||
holder.binding.imageProductPicture.setFocusable(false);
|
||||
holder.binding.imageProductPicture.setFocusableInTouchMode(false);
|
||||
holder.binding.imageProductPicture.setOnClickListener(new ImageProductPictureOnClickListener(position));
|
||||
} else {
|
||||
holder.binding.imageProductPicture.setOnClickListener(null);
|
||||
|
||||
@ -117,7 +117,9 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
|
||||
}
|
||||
|
||||
if (count > 0 && !isLock) {
|
||||
holder.binding.imageProductPicture.setOnClickListener(new imageProductPictureOnClickListener(position));
|
||||
holder.binding.imageProductPicture.setFocusable(false);
|
||||
holder.binding.imageProductPicture.setFocusableInTouchMode(false);
|
||||
holder.binding.imageProductPicture.setOnClickListener(new imageProductPictureOnClickListener(position));
|
||||
} else {
|
||||
holder.binding.imageProductPicture.setOnClickListener(null);
|
||||
}
|
||||
|
||||
@ -336,10 +336,14 @@ public class CheckoutNfcCardDialog extends DialogAbstract {
|
||||
}
|
||||
|
||||
public void cancelOnThreadCountdown() {
|
||||
if (nfcPayController != null) {
|
||||
nfcPayController.shutdown();
|
||||
}
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
cancel();
|
||||
}
|
||||
|
||||
|
||||
public void setButtonCancelText(long countdown) {
|
||||
binding.buttonCancel.setText("取消("+countdown+")");
|
||||
}
|
||||
|
||||
@ -339,10 +339,14 @@ public class CheckoutNfcPhoneDialog extends DialogAbstract {
|
||||
}
|
||||
|
||||
public void cancelOnThreadCountdown() {
|
||||
if (nfcPayController != null) {
|
||||
nfcPayController.shutdown();
|
||||
}
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
cancel();
|
||||
}
|
||||
|
||||
|
||||
public void setButtonCancelText(long countdown) {
|
||||
binding.buttonCancel.setText("取消("+countdown+")");
|
||||
}
|
||||
|
||||
@ -337,10 +337,14 @@ public class CheckoutNfcRechargeDialog extends DialogAbstract {
|
||||
}
|
||||
|
||||
public void cancelOnThreadCountdown() {
|
||||
if (nfcPayController != null) {
|
||||
nfcPayController.shutdown();
|
||||
}
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
cancel();
|
||||
}
|
||||
|
||||
|
||||
public void setButtonCancelText(long countdown) {
|
||||
binding.buttonCancel.setText("取消("+countdown+")");
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user