Files
cursornew2026/参考计费/Packages/VibeviewerModel/Sources/VibeviewerModel/Entities/DashboardSnapshot.swift
ccdojox-crypto 73a71f198f 蜂鸟Pro v2.0.1 - 基础框架版本 (待完善)
## 当前状态
- 插件界面已完成重命名 (cursorpro → hummingbird)
- 双账号池 UI 已实现 (Auto/Pro 卡片)
- 后端已切换到 MySQL 数据库
- 添加了 Cursor 官方用量 API 文档

## 已知问题 (待修复)
1. 激活时检查账号导致无账号时激活失败
2. 未启用无感换号时不应获取账号
3. 账号用量模块不显示 (seamless 未启用时应隐藏)
4. 积分显示为 0 (后端未正确返回)
5. Auto/Pro 双密钥逻辑混乱,状态不同步
6. 账号添加后无自动分析功能

## 下一版本计划
- 重构数据模型,优化账号状态管理
- 实现 Cursor API 自动分析账号
- 修复激活流程,不依赖账号
- 启用无感时才分配账号
- 完善账号用量实时显示

## 文件说明
- docs/系统设计文档.md - 完整架构设计
- cursor 官方用量接口.md - Cursor API 文档
- 参考计费/ - Vibeviewer 开源项目参考

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:21:52 +08:00

166 lines
7.3 KiB
Swift
Raw 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.

import Foundation
@Observable
public class DashboardSnapshot: Codable, Equatable {
//
public let email: String
/// ( + (Billing))
public let totalRequestsAllModels: Int
///
public let spendingCents: Int
///
public let hardLimitDollars: Int
///
public let usageEvents: [UsageEvent]
/// usageEvents
public let requestToday: Int
/// usageEvents
public let requestYestoday: Int
/// 使
public let usageSummary: UsageSummary?
/// Team Plan
public let freeUsageCents: Int
/// 使
public let modelsUsageChart: ModelsUsageChartData?
/// Pro Team
public let modelsUsageSummary: ModelsUsageSummary?
///
public let billingCycleStartMs: String?
///
public let billingCycleEndMs: String?
public init(
email: String,
totalRequestsAllModels: Int,
spendingCents: Int,
hardLimitDollars: Int,
usageEvents: [UsageEvent] = [],
requestToday: Int = 0,
requestYestoday: Int = 0,
usageSummary: UsageSummary? = nil,
freeUsageCents: Int = 0,
modelsUsageChart: ModelsUsageChartData? = nil,
modelsUsageSummary: ModelsUsageSummary? = nil,
billingCycleStartMs: String? = nil,
billingCycleEndMs: String? = nil
) {
self.email = email
self.totalRequestsAllModels = totalRequestsAllModels
self.spendingCents = spendingCents
self.hardLimitDollars = hardLimitDollars
self.usageEvents = usageEvents
self.requestToday = requestToday
self.requestYestoday = requestYestoday
self.usageSummary = usageSummary
self.freeUsageCents = freeUsageCents
self.modelsUsageChart = modelsUsageChart
self.modelsUsageSummary = modelsUsageSummary
self.billingCycleStartMs = billingCycleStartMs
self.billingCycleEndMs = billingCycleEndMs
}
private enum CodingKeys: String, CodingKey {
case email
case totalRequestsAllModels
case spendingCents
case hardLimitDollars
case usageEvents
case requestToday
case requestYestoday
case usageSummary
case freeUsageCents
case modelsUsageChart
case modelsUsageSummary
case billingCycleStartMs
case billingCycleEndMs
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.email = try container.decode(String.self, forKey: .email)
self.totalRequestsAllModels = try container.decode(Int.self, forKey: .totalRequestsAllModels)
self.spendingCents = try container.decode(Int.self, forKey: .spendingCents)
self.hardLimitDollars = try container.decode(Int.self, forKey: .hardLimitDollars)
self.requestToday = try container.decode(Int.self, forKey: .requestToday)
self.requestYestoday = try container.decode(Int.self, forKey: .requestYestoday)
self.usageEvents = try container.decode([UsageEvent].self, forKey: .usageEvents)
self.usageSummary = try? container.decode(UsageSummary.self, forKey: .usageSummary)
self.freeUsageCents = (try? container.decode(Int.self, forKey: .freeUsageCents)) ?? 0
self.modelsUsageChart = try? container.decode(ModelsUsageChartData.self, forKey: .modelsUsageChart)
self.modelsUsageSummary = try? container.decode(ModelsUsageSummary.self, forKey: .modelsUsageSummary)
self.billingCycleStartMs = try? container.decode(String.self, forKey: .billingCycleStartMs)
self.billingCycleEndMs = try? container.decode(String.self, forKey: .billingCycleEndMs)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.email, forKey: .email)
try container.encode(self.totalRequestsAllModels, forKey: .totalRequestsAllModels)
try container.encode(self.spendingCents, forKey: .spendingCents)
try container.encode(self.hardLimitDollars, forKey: .hardLimitDollars)
try container.encode(self.usageEvents, forKey: .usageEvents)
try container.encode(self.requestToday, forKey: .requestToday)
try container.encode(self.requestYestoday, forKey: .requestYestoday)
if let usageSummary = self.usageSummary {
try container.encode(usageSummary, forKey: .usageSummary)
}
if self.freeUsageCents > 0 {
try container.encode(self.freeUsageCents, forKey: .freeUsageCents)
}
if let modelsUsageChart = self.modelsUsageChart {
try container.encode(modelsUsageChart, forKey: .modelsUsageChart)
}
if let modelsUsageSummary = self.modelsUsageSummary {
try container.encode(modelsUsageSummary, forKey: .modelsUsageSummary)
}
if let billingCycleStartMs = self.billingCycleStartMs {
try container.encode(billingCycleStartMs, forKey: .billingCycleStartMs)
}
if let billingCycleEndMs = self.billingCycleEndMs {
try container.encode(billingCycleEndMs, forKey: .billingCycleEndMs)
}
}
/// plan + onDemand
public var totalUsageCents: Int {
guard let usageSummary = usageSummary else {
return spendingCents
}
let planUsed = usageSummary.individualUsage.plan.used
let onDemandUsed = usageSummary.individualUsage.onDemand?.used ?? 0
let freeUsage = freeUsageCents
return planUsed + onDemandUsed + freeUsage
}
/// UI
/// - Pro pro / proPlus / ultra `modelsUsageSummary`
/// 使 `ModelUsageInfo`
/// - 退 `totalUsageCents`
public var displayTotalUsageCents: Int {
if
let usageSummary,
let modelsUsageSummary,
usageSummary.membershipType.isProSeries
{
return Int(modelsUsageSummary.totalCostCents.rounded())
}
return totalUsageCents
}
public static func == (lhs: DashboardSnapshot, rhs: DashboardSnapshot) -> Bool {
lhs.email == rhs.email &&
lhs.totalRequestsAllModels == rhs.totalRequestsAllModels &&
lhs.spendingCents == rhs.spendingCents &&
lhs.hardLimitDollars == rhs.hardLimitDollars &&
lhs.usageSummary == rhs.usageSummary &&
lhs.freeUsageCents == rhs.freeUsageCents &&
lhs.modelsUsageChart == rhs.modelsUsageChart &&
lhs.modelsUsageSummary == rhs.modelsUsageSummary &&
lhs.billingCycleStartMs == rhs.billingCycleStartMs &&
lhs.billingCycleEndMs == rhs.billingCycleEndMs
}
}