Initial commit: suixinkan_ios UIKit rewrite project.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 14:33:31 +08:00
commit 9edf993432
297 changed files with 47151 additions and 0 deletions

View File

@ -0,0 +1,206 @@
//
// 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 {
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)
}
private static func parseAmount(_ text: String) -> Double {
let normalized = text.filter { "0123456789.-".contains($0) }
return Double(normalized) ?? 0
}
}
/// 退
struct StatisticsDailyItem: Decodable, Identifiable, Equatable {
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)
}
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
}
}