Implement orders tab aligned with Android, including scan verify flows.

Add role-based order/deposit lists, AVFoundation QR scanning, verify dialogs, and ViewModel tests so photographers, scenic admins, and store admins match Android behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 16:59:53 +08:00
parent 6492f80ef0
commit 31d2b6094b
27 changed files with 4226 additions and 17 deletions

View File

@ -0,0 +1,73 @@
//
// QRCodeScanResult.swift
// suixinkan
//
import Foundation
/// JSON Android QR
struct QRCodeScanResult: Equatable {
let rawJSON: String
let method: String
let orderNumber: String
let userId: Int
let time: String
let timeLong: Int64
let checkSum: String
static func parse(_ content: String) -> QRCodeScanResult? {
let trimmed = content.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty, let data = trimmed.data(using: .utf8) else { return nil }
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil }
let method = (json["method"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let orderNumber = (json["order_number"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let userId = Self.parseInt(json["user_id"])
let timeLong = Self.parseTimeLong(json["time"])
let time = Self.parseTimeString(json["time"])
let checkSum = (json["check_sum"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return QRCodeScanResult(
rawJSON: trimmed,
method: method,
orderNumber: orderNumber,
userId: userId,
time: time,
timeLong: timeLong,
checkSum: checkSum
)
}
private static func parseInt(_ value: Any?) -> Int {
if let intValue = value as? Int { return intValue }
if let stringValue = value as? String, let intValue = Int(stringValue.trimmingCharacters(in: .whitespacesAndNewlines)) {
return intValue
}
if let doubleValue = value as? Double { return Int(doubleValue) }
return 0
}
private static func parseTimeLong(_ value: Any?) -> Int64 {
if let intValue = value as? Int64 { return intValue }
if let intValue = value as? Int { return Int64(intValue) }
if let doubleValue = value as? Double { return Int64(doubleValue) }
if let stringValue = value as? String, let intValue = Int64(stringValue.trimmingCharacters(in: .whitespacesAndNewlines)) {
return intValue
}
return 0
}
private static func parseTimeString(_ value: Any?) -> String {
if let stringValue = value as? String {
return stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
}
let longValue = parseTimeLong(value)
guard longValue > 0 else { return "" }
let seconds = longValue > 1_000_000_000_000 ? TimeInterval(longValue) / 1000 : TimeInterval(longValue)
let date = Date(timeIntervalSince1970: seconds)
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "zh_CN")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter.string(from: date)
}
}