实现订单 Tab 并接入扫码核销流程
This commit is contained in:
203
suixinkan/Features/Orders/Utils/MainTabScanHandler.swift
Normal file
203
suixinkan/Features/Orders/Utils/MainTabScanHandler.swift
Normal file
@ -0,0 +1,203 @@
|
||||
//
|
||||
// MainTabScanHandler.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
import UIKit
|
||||
|
||||
/// 主 Tab 中间扫码按钮结果处理,对齐 Android `MainTabViewModel.signIn`。
|
||||
@MainActor
|
||||
final class MainTabScanHandler: NSObject, CLLocationManagerDelegate {
|
||||
|
||||
var onShowMessage: ((String) -> Void)?
|
||||
var onShowSignIn: ((OrderSignInResponse) -> Void)?
|
||||
var onShowWriteOff: ((WriteOffOrderResponse) -> Void)?
|
||||
var onMultiVerifyPending: ((MultiVerifyParseData) -> Void)?
|
||||
var onShowMultiVerifySheet: (([MultiTravelScenicSpotEntity], MultiTravelScenicSpotEntity?) -> Void)?
|
||||
|
||||
private let api: OrderAPI
|
||||
private let locationManager = CLLocationManager()
|
||||
private var locationContinuation: ((Result<CLLocationCoordinate2D, Error>) -> Void)?
|
||||
private(set) var pendingMultiVerify: MultiVerifyParseData?
|
||||
|
||||
init(api: OrderAPI) {
|
||||
self.api = api
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBest
|
||||
}
|
||||
|
||||
func handleScanResult(_ scanResult: String) async {
|
||||
guard !scanResult.isEmpty else {
|
||||
onShowMessage?("请扫描正确的二维码")
|
||||
return
|
||||
}
|
||||
|
||||
guard let parsed = QRCodeScanResult.parse(scanResult) else {
|
||||
onShowMessage?("请扫描正确的二维码")
|
||||
return
|
||||
}
|
||||
|
||||
switch parsed.method {
|
||||
case "check_in":
|
||||
await performCheckIn(scanResult: scanResult)
|
||||
case "order_verify":
|
||||
if AppStore.shared.currentAppRole == .storeAdmin {
|
||||
await performStoreVerify(parsed: parsed)
|
||||
} else {
|
||||
await performPhotographerVerify(jsonBody: parsed.rawJSON)
|
||||
}
|
||||
case "multi_verify":
|
||||
guard !parsed.orderNumber.isEmpty, parsed.userId > 0, parsed.timeLong > 0, !parsed.checkSum.isEmpty else {
|
||||
onShowMessage?("请扫描正确的多点位核销码")
|
||||
return
|
||||
}
|
||||
let pending = MultiVerifyParseData(
|
||||
orderNumber: parsed.orderNumber,
|
||||
userId: parsed.userId,
|
||||
time: parsed.timeLong,
|
||||
checkSum: parsed.checkSum
|
||||
)
|
||||
pendingMultiVerify = pending
|
||||
onMultiVerifyPending?(pending)
|
||||
await fetchSpotsForMultiVerify(pending: pending)
|
||||
case SaleUserQrParser.methodSaleUserBind:
|
||||
if let id = SaleUserQrParser.parseSaleUserId(scanResult), id > 0 {
|
||||
onShowMessage?("获客员绑定功能待接入")
|
||||
} else {
|
||||
onShowMessage?("请扫描正确的获客员二维码")
|
||||
}
|
||||
default:
|
||||
onShowMessage?("未知二维码,请检查后重试")
|
||||
}
|
||||
}
|
||||
|
||||
func confirmMultiVerify(pending: MultiVerifyParseData, spot: MultiTravelScenicSpotEntity) async {
|
||||
do {
|
||||
let response = try await api.multiTravelOrderVerify(
|
||||
MultiTravelOrderVerifyRequest(
|
||||
orderNumber: pending.orderNumber,
|
||||
userId: pending.userId,
|
||||
time: pending.time,
|
||||
checkSum: pending.checkSum,
|
||||
scenicSpotId: spot.scenicSpotId
|
||||
)
|
||||
)
|
||||
onShowSignIn?(response)
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func performCheckIn(scanResult: String) async {
|
||||
guard var json = parseJSONObject(scanResult) else {
|
||||
onShowMessage?("请扫描正确的二维码")
|
||||
return
|
||||
}
|
||||
json["scenic_id"] = String(AppStore.shared.currentScenicId)
|
||||
guard let body = serializeJSON(json) else { return }
|
||||
do {
|
||||
let response = try await api.orderCheckIn(jsonBody: body)
|
||||
onShowSignIn?(response)
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func performPhotographerVerify(jsonBody: String) async {
|
||||
do {
|
||||
let response = try await api.orderVerify(jsonBody: jsonBody)
|
||||
onShowWriteOff?(response)
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func performStoreVerify(parsed: QRCodeScanResult) async {
|
||||
guard parsed.userId > 0, !parsed.orderNumber.isEmpty, !parsed.time.isEmpty else {
|
||||
onShowMessage?("请扫描正确的核销码")
|
||||
return
|
||||
}
|
||||
let storeId = AppStore.shared.currentStoreId
|
||||
guard storeId > 0 else {
|
||||
onShowMessage?("店铺信息异常,请先选择店铺")
|
||||
return
|
||||
}
|
||||
do {
|
||||
let response = try await api.storeOrderVerify(
|
||||
StoreOrderVerifyRequest(
|
||||
userId: parsed.userId,
|
||||
orderNumber: parsed.orderNumber,
|
||||
time: parsed.time,
|
||||
storeId: storeId
|
||||
)
|
||||
)
|
||||
onShowWriteOff?(response)
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchSpotsForMultiVerify(pending: MultiVerifyParseData) async {
|
||||
let status = locationManager.authorizationStatus
|
||||
guard status == .authorizedWhenInUse || status == .authorizedAlways else {
|
||||
locationManager.requestWhenInUseAuthorization()
|
||||
onShowMessage?("缺少定位权限,功能无法进行")
|
||||
return
|
||||
}
|
||||
do {
|
||||
let coordinate = try await requestLocation()
|
||||
let response = try await api.multiTravelScenicSpotList(
|
||||
orderNumber: pending.orderNumber,
|
||||
lat: coordinate.latitude,
|
||||
lng: coordinate.longitude
|
||||
)
|
||||
let spots = response.entityList
|
||||
if spots.isEmpty {
|
||||
onShowMessage?("暂无可核销的打卡点")
|
||||
} else {
|
||||
onShowMultiVerifySheet?(spots, spots.first)
|
||||
}
|
||||
} catch {
|
||||
onShowMessage?(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func requestLocation() async throws -> CLLocationCoordinate2D {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
locationContinuation = { result in
|
||||
switch result {
|
||||
case .success(let coordinate):
|
||||
continuation.resume(returning: coordinate)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
locationManager.requestLocation()
|
||||
}
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
guard let location = locations.last else { return }
|
||||
locationContinuation?(.success(location.coordinate))
|
||||
locationContinuation = nil
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
||||
locationContinuation?(.failure(error))
|
||||
locationContinuation = nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension MainTabScanHandler {
|
||||
func parseJSONObject(_ text: String) -> [String: Any]? {
|
||||
guard let data = text.data(using: .utf8) else { return nil }
|
||||
return try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
||||
}
|
||||
|
||||
func serializeJSON(_ json: [String: Any]) -> String? {
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: json) else { return nil }
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user