diff --git a/app/src/General/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java b/app/src/General/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java index 1731703..05b2fa7 100644 --- a/app/src/General/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java +++ b/app/src/General/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java @@ -313,15 +313,7 @@ public class FontendPickupCodeDialog extends DialogAbstract { String barCodeString = new String(buffer, 0, size); getHandlerMain().start(getLogs().getClassName(), getCtx().getString(R.string.receive_barcode)+":"+ Tools.randomReplace(barCodeString, 0.3)+","+getCtx().getString(R.string.send_transaction));//收到條碼,送出交易 - try { - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(barCodeString); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException | LogsSettingEmptyException e) { - getLogs().warning(e); - } + submitPickupCode(barCodeString.trim()); openBarcode = false; } @@ -357,23 +349,65 @@ public class FontendPickupCodeDialog extends DialogAbstract { } public void changeOk() { - try { - String number = binding.editTextNumber.getText().toString(); - // 長度檢查 - if (number.length() < 8) { - getHandlerMain().start(getClass().getSimpleName(), "統一編號長度不能少於 8 碼"); - return; - } - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(number); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); + String number = binding.editTextNumber.getText().toString().trim(); + // 長度檢查 + if (number.isEmpty() || number.length() < 8) { + getHandlerMain().start(getClass().getSimpleName(), "取貨碼長度不能少於 8 碼"); + return; } + submitPickupCode(number); + } + + /** + * 取貨碼驗證改走 StarCloudAPI B660(邏輯比照員工卡 B680)。 + * 成功回傳 code_id 與要出貨的 slot_no:據此建立 BuyList、保存 code_id 供 MQTT + * finalize(payment_type=6 取貨碼),再進入取貨確認 → 出貨。 + */ + private void submitPickupCode(String code) { + new com.unibuy.smartdevice.external.StarCloudAPI(getHandlerMain(), getHandlerMain()) + .verifyPickupCode(code, new com.unibuy.smartdevice.external.StarCloudAPI.PickupVerificationCallback() { + @Override + public void onSuccess(String codeId, int slotNo) { + try { + SlotStructure slotProduct = MyApp.getInstance().getSlotData(SlotField.VMC.getField(), slotNo); + if (slotProduct == null) { + setStatusText(getContext().getString(R.string.product_not_found)); + openBarcode = true; + return; + } + if (slotProduct.isLock()) { + getSrcHandlerMain().start("FontendPickupCodeDialog", "@" + getContext().getString(R.string.product_sales_suspended)); + dialogCancel(); + return; + } + if (slotProduct.getCount() == 0) { + getSrcHandlerMain().start("FontendPickupCodeDialog", "@" + getContext().getString(R.string.product_out_of_stock)); + dialogCancel(); + return; + } + MyApp.getInstance().getBuyList().clear(); + MyApp.getInstance().getBuyList().add( + new BuyStructure(slotProduct.getField(), slotProduct.getSlot(), slotProduct.getProduct(), 1)); + + MyApp.getInstance().getReportFlowInfoData().setFlowType(6); // 取貨碼 → payment_type 6 + MyApp.getInstance().getReportFlowInfoData().setFlowBarCode(code); + MyApp.getInstance().getReportFlowInfoData().setReportFlowId(code); + MyApp.getInstance().getReportFlowInfoData().setOrderId(Tools.generateTimeBasedUUID()); + MyApp.getInstance().getReportAccessCodeStructure().setAccessCodeId(codeId); + + changeBuy(); // 開啟取貨確認 BuyPickupCodeDialog → 出貨 + } catch (LogsEmptyException e) { + setStatusText(getContext().getString(R.string.product_not_found)); + openBarcode = true; + } + } + + @Override + public void onFailure(String message) { + openBarcode = true; + setStatusText(message); + } + }); } public void changeBuy() { diff --git a/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java b/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java index 76f11a3..5a33a11 100644 --- a/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java +++ b/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java @@ -593,6 +593,187 @@ public class StarCloudAPI { void onFailure(String message); } + /** 取貨碼驗證回呼:成功時回傳 code_id 與要出貨的貨道 slot_no。 */ + public interface PickupVerificationCallback { + void onSuccess(String codeId, int slotNo); + void onFailure(String message); + } + + /** + * B660: 取貨碼驗證(邏輯比照 B680 員工卡,驗證通道不同)。 + * 成功回傳 data.code_id 與 data.slot_no(要出貨的貨道)。 + */ + public void verifyPickupCode(final String code, final PickupVerificationCallback callback) { + final String url = getBaseUrl() + "machine/pickup/verify/B660"; + Log.i(TAG, "Starting B660 request for code: " + code); + + new Thread(new Runnable() { + @Override + public void run() { + int responseCode = -1; + String receive = ""; + try { + JSONObject json = new JSONObject(); + json.put("code", code); + + java.net.URL reqUrl = new java.net.URL(url); + java.net.HttpURLConnection conn = (java.net.HttpURLConnection) reqUrl.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + conn.setRequestProperty("Accept", "application/json"); + String machineToken = MyApp.getInstance().getMachine().getApiKey(); + if (machineToken != null && !machineToken.isEmpty()) { + conn.setRequestProperty("Authorization", "Bearer " + machineToken); + } + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + conn.setDoOutput(true); + conn.getOutputStream().write(json.toString().getBytes("UTF-8")); + conn.getOutputStream().flush(); + + responseCode = conn.getResponseCode(); + java.io.InputStream is = (responseCode >= 200 && responseCode < 300) + ? conn.getInputStream() : conn.getErrorStream(); + if (is != null) { + java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(is, "UTF-8")); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) sb.append(line); + receive = sb.toString(); + } + conn.disconnect(); + Log.d(TAG, "B660 Response (" + responseCode + "): " + receive); + } catch (final Exception e) { + Log.e(TAG, "B660 Network Error", e); + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> callback.onFailure("連線失敗: " + e.getMessage())); + return; + } + + final int finalCode = responseCode; + final String finalReceive = receive; + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (finalCode >= 200 && finalCode < 300) { + try { + JSONObject res = new JSONObject(finalReceive); + if (res.optBoolean("success", false)) { + JSONObject data = res.optJSONObject("data"); + String codeId = data != null ? data.optString("code_id", "") : ""; + int slotNo = data != null ? data.optInt("slot_no", -1) : -1; + callback.onSuccess(codeId, slotNo); + } else { + callback.onFailure(res.optString("message", "取貨碼驗證失敗")); + } + } catch (Exception e) { + callback.onFailure("解析錯誤: " + finalCode); + } + } else if (finalCode == 404) { + callback.onFailure("查無此取貨碼或已使用"); + } else if (finalCode == 429) { + callback.onFailure("錯誤次數過多,請稍後再試"); + } else if (finalCode == 401) { + callback.onFailure("系統認證失效,請聯繫管理員"); + } else { + try { + JSONObject res = new JSONObject(finalReceive); + callback.onFailure(res.optString("message", "系統連線錯誤 (" + finalCode + ")")); + } catch (Exception e) { + callback.onFailure("系統連線錯誤 (" + finalCode + ")"); + } + } + }); + } + }).start(); + } + + /** + * B670: 通行碼驗證(技師用,邏輯比照 B680,驗證通道不同)。 + * 成功回傳 data.code_id。 + */ + public void verifyPassCode(final String code, final VerificationCallback callback) { + final String url = getBaseUrl() + "machine/passcode/verify/B670"; + Log.i(TAG, "Starting B670 request for code: " + code); + + new Thread(new Runnable() { + @Override + public void run() { + int responseCode = -1; + String receive = ""; + try { + JSONObject json = new JSONObject(); + json.put("code", code); + + java.net.URL reqUrl = new java.net.URL(url); + java.net.HttpURLConnection conn = (java.net.HttpURLConnection) reqUrl.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + conn.setRequestProperty("Accept", "application/json"); + String machineToken = MyApp.getInstance().getMachine().getApiKey(); + if (machineToken != null && !machineToken.isEmpty()) { + conn.setRequestProperty("Authorization", "Bearer " + machineToken); + } + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + conn.setDoOutput(true); + conn.getOutputStream().write(json.toString().getBytes("UTF-8")); + conn.getOutputStream().flush(); + + responseCode = conn.getResponseCode(); + java.io.InputStream is = (responseCode >= 200 && responseCode < 300) + ? conn.getInputStream() : conn.getErrorStream(); + if (is != null) { + java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(is, "UTF-8")); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) sb.append(line); + receive = sb.toString(); + } + conn.disconnect(); + Log.d(TAG, "B670 Response (" + responseCode + "): " + receive); + } catch (final Exception e) { + Log.e(TAG, "B670 Network Error", e); + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> callback.onFailure("連線失敗: " + e.getMessage())); + return; + } + + final int finalCode = responseCode; + final String finalReceive = receive; + new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { + if (finalCode >= 200 && finalCode < 300) { + try { + JSONObject res = new JSONObject(finalReceive); + if (res.optBoolean("success", false)) { + String codeId = ""; + if (res.has("data")) { + codeId = res.optJSONObject("data").optString("code_id", ""); + } else { + codeId = res.optString("code_id", ""); + } + callback.onSuccess(codeId); + } else { + callback.onFailure(res.optString("message", "通行碼驗證失敗")); + } + } catch (Exception e) { + callback.onFailure("解析錯誤: " + finalCode); + } + } else if (finalCode == 404) { + callback.onFailure("查無此通行碼或已失效"); + } else if (finalCode == 429) { + callback.onFailure("錯誤次數過多,請稍後再試"); + } else if (finalCode == 401) { + callback.onFailure("系統認證失效,請聯繫管理員"); + } else { + try { + JSONObject res = new JSONObject(finalReceive); + callback.onFailure(res.optString("message", "系統連線錯誤 (" + finalCode + ")")); + } catch (Exception e) { + callback.onFailure("系統連線錯誤 (" + finalCode + ")"); + } + } + }); + } + }).start(); + } + private void saveSettings(JSONObject config) { // 使用 optString 避免欄位缺失導致 JSONException diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/dialog/CheckoutAccessCodeDialog.java b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/CheckoutAccessCodeDialog.java index 0d63a9e..d21728d 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/dialog/CheckoutAccessCodeDialog.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/CheckoutAccessCodeDialog.java @@ -272,12 +272,7 @@ public class CheckoutAccessCodeDialog extends DialogAbstract { String barCodeString = new String(buffer, 0, size); getHandlerMain().start(getLogs().getClassName(), getContext().getString(R.string.receive_barcode)+":"+ Tools.randomReplace(barCodeString, 0.3)+","+getContext().getString(R.string.send_transaction));//收到條碼,送出交易 - try { - MyApp.getInstance().getAccessCodeStructure().setPassCode(barCodeString); - httpAPI.checkAccessCode(MyApp.getInstance().getAccessCodeStructure()); - } catch (LogsParseException | LogsSettingEmptyException e) { - getLogs().warning(e); - } + submitPassCode(barCodeString.trim()); openBarcode = false; } @@ -329,13 +324,36 @@ public class CheckoutAccessCodeDialog extends DialogAbstract { getHandlerMain().start(getClass().getSimpleName(), "通行碼長度不能少於 8 碼"); return; } - MyApp.getInstance().getAccessCodeStructure().setPassCode(number); - httpAPI.checkAccessCode(MyApp.getInstance().getAccessCodeStructure()); - } catch (LogsParseException | LogsSettingEmptyException e) { + submitPassCode(number); + } catch (Exception e) { getLogs().warning(e); } } + /** + * 通行碼驗證改走 StarCloudAPI B670(邏輯比照員工卡 B680)。 + * 成功後保存 code_id 供 MQTT finalize(payment_type=5 通行碼),再進入出貨。 + */ + private void submitPassCode(String code) { + new com.unibuy.smartdevice.external.StarCloudAPI(getHandlerMain(), getHandlerMain()) + .verifyPassCode(code, new com.unibuy.smartdevice.external.StarCloudAPI.VerificationCallback() { + @Override + public void onSuccess(String codeId) { + MyApp.getInstance().getReportFlowInfoData().setFlowType(5); // 通行碼 → payment_type 5 + MyApp.getInstance().getReportFlowInfoData().setFlowBarCode(code); + MyApp.getInstance().getReportFlowInfoData().setReportFlowId(code); + MyApp.getInstance().getReportAccessCodeStructure().setAccessCodeId(codeId); + getHandlerMain().send(getClass().getSimpleName(), Option.CHANGE_DISPATCH.getOption(), "change dispatch"); + } + + @Override + public void onFailure(String message) { + openBarcode = true; // 允許重新掃碼/輸入 + getHandlerMain().start(getClass().getSimpleName(), Option.SET_STATUS_TEXT.getOption(), message); + } + }); + } + public void changeBuy() { closeDialogCountdown.shutdown(); changeDialog(BuyDialog.class);