Files
汉秋 d99a5b1bf8 Advance UIKit rewrite with AMap integration and core UI modules.
Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 15:16:12 +08:00

211 lines
7.2 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// StatisticsModels.swift
// suixinkan
//
// Created by Codex on 2026/6/22.
//
import Foundation
///
enum StatisticsPeriod: String, CaseIterable, Identifiable {
case today = "今日"
case yesterday = "昨日"
case sevenDays = "7日"
case thisMonth = "本月"
var id: String { rawValue }
///
var apiRange: String {
switch self {
case .today:
return "1"
case .yesterday:
return "2"
case .sevenDays:
return "3"
case .thisMonth:
return "4"
}
}
///
var interval: (start: Date, end: Date) {
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
switch self {
case .today:
return (today, today)
case .yesterday:
let day = calendar.date(byAdding: .day, value: -1, to: today) ?? today
return (day, day)
case .sevenDays:
return (calendar.date(byAdding: .day, value: -6, to: today) ?? today, today)
case .thisMonth:
let components = calendar.dateComponents([.year, .month], from: today)
return (calendar.date(from: components) ?? today, today)
}
}
///
var selectedTimeText: String {
let dates = interval
return "\(Self.uiDateFormatter.string(from: dates.start))\(Self.uiDateFormatter.string(from: dates.end))"
}
///
var apiStartTime: String {
Self.apiDateFormatter.string(from: interval.start)
}
///
var apiEndTime: String {
Self.apiDateFormatter.string(from: interval.end)
}
private static let uiDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "zh_CN")
formatter.dateFormat = "yyyy/MM/dd"
return formatter
}()
private static let apiDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
}
/// 退
struct StatisticsSummaryResponse: Decodable, Equatable, Hashable {
let orderAmountSum: String
let orderCount: Int
let orderPriceAverage: String
let receivedAmountSum: String
let refundAmountSum: String
var orderAmountValue: Double { Self.parseAmount(orderAmountSum) }
var orderPriceAverageValue: Double { Self.parseAmount(orderPriceAverage) }
var receivedAmountValue: Double { Self.parseAmount(receivedAmountSum) }
var refundAmountValue: Double { Self.parseAmount(refundAmountSum) }
///
enum CodingKeys: String, CodingKey {
case orderAmountSum = "order_amount_sum"
case orderCount = "order_count"
case orderPriceAverage = "order_price_avg"
case receivedAmountSum = "received_amount_sum"
case refundAmountSum = "refund_amount_sum"
}
///
init(
orderAmountSum: String = "",
orderCount: Int = 0,
orderPriceAverage: String = "",
receivedAmountSum: String = "",
refundAmountSum: String = ""
) {
self.orderAmountSum = orderAmountSum
self.orderCount = orderCount
self.orderPriceAverage = orderPriceAverage
self.receivedAmountSum = receivedAmountSum
self.refundAmountSum = refundAmountSum
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
orderAmountSum = try container.decodeLossyString(forKey: .orderAmountSum)
orderCount = try container.decodeLossyInt(forKey: .orderCount) ?? 0
orderPriceAverage = try container.decodeLossyString(forKey: .orderPriceAverage)
receivedAmountSum = try container.decodeLossyString(forKey: .receivedAmountSum)
refundAmountSum = try container.decodeLossyString(forKey: .refundAmountSum)
}
/// Amount
private static func parseAmount(_ text: String) -> Double {
let normalized = text.filter { "0123456789.-".contains($0) }
return Double(normalized) ?? 0
}
}
/// 退
struct StatisticsDailyItem: Decodable, Identifiable, Equatable, Hashable {
var id: String { date }
let date: String
let orderCount: Int
let orderPrice: String
let refund: String
let received: String
var orderPriceValue: Double { Self.parseAmount(orderPrice) }
var refundValue: Double { Self.parseAmount(refund) }
var receivedValue: Double { Self.parseAmount(received) }
///
enum CodingKeys: String, CodingKey {
case date
case orderCount = "order_count"
case orderPrice = "order_price"
case refund
case received
}
///
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
date = try container.decodeLossyString(forKey: .date)
orderCount = try container.decodeLossyInt(forKey: .orderCount) ?? 0
orderPrice = try container.decodeLossyString(forKey: .orderPrice)
refund = try container.decodeLossyString(forKey: .refund)
received = try container.decodeLossyString(forKey: .received)
}
/// Amount
private static func parseAmount(_ text: String) -> Double {
let normalized = text.filter { "0123456789.-".contains($0) }
return Double(normalized) ?? 0
}
}
private extension KeyedDecodingContainer {
/// JSON
func decodeLossyString(forKey key: Key) throws -> String {
if let value = try? decodeIfPresent(String.self, forKey: key) {
return value
}
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return String(value)
}
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return String(value)
}
return ""
}
/// IntDouble
func decodeLossyInt(forKey key: Key) throws -> Int? {
if let value = try? decodeIfPresent(Int.self, forKey: key) {
return value
}
if let value = try? decodeIfPresent(String.self, forKey: key) {
let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
if let intValue = Int(text) {
return intValue
}
if let doubleValue = Double(text) {
return Int(doubleValue)
}
}
if let value = try? decodeIfPresent(Double.self, forKey: key) {
return Int(value)
}
return nil
}
}