b2970e4c-45a2-423e-b84d-257.../Sources/storage/GoldPositionStore.swift
徐翔宇 e5a72bcf42 v0.2.0: initial 看盘侠 — Fund & Gold plugin for MioIsland
Real-time OTC fund and gold tracker for the macOS notch. Three tabs:
- 持仓: live intraday estimates from 天天基金, hero card with
  total value / today P&L / cumulative P&L, per-row 当日 + 累计.
- 黄金: SHFE 沪金 realtime + daily K-line + 伦敦金 reference.
- 添加: search 26k+ public funds via 东方财富 suggest API.

No API keys, no Python, no servers. All data comes from public
endpoints (天天基金, 东方财富, 新浪财经).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:20:15 +08:00

58 lines
2.0 KiB
Swift

//
// GoldPositionStore.swift
// plugin v0.2
//
// Persists the single GoldPosition to its own JSON file (separate
// from watchlist.json different shape, different lifecycle).
//
// Path: ~/Library/Application Support/Mio Island/Fund/gold-position.json
//
import Foundation
@MainActor
final class GoldPositionStore: ObservableObject {
@Published private(set) var position: GoldPosition = GoldPosition(grams: nil, costPerGram: nil)
private let storeURL: URL
private let queue = DispatchQueue(label: "com.mioisland.plugin.fund.gold-position", qos: .userInitiated)
init() {
let appSupport = FileManager.default
.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
let dir = appSupport.appendingPathComponent("Mio Island/Fund", isDirectory: true)
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
self.storeURL = dir.appendingPathComponent("gold-position.json")
load()
}
func update(grams: Double?, costPerGram: Double?) {
position = GoldPosition(grams: grams, costPerGram: costPerGram)
save()
}
private func load() {
guard let data = try? Data(contentsOf: storeURL),
let decoded = try? JSONDecoder().decode(GoldPosition.self, from: data) else {
return
}
position = decoded
}
private func save() {
let snapshot = position
queue.async { [storeURL] in
do {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(snapshot)
let tmp = storeURL.appendingPathExtension("tmp")
try data.write(to: tmp, options: .atomic)
_ = try? FileManager.default.replaceItemAt(storeURL, withItemAt: tmp)
} catch {
NSLog("[fund-plugin] gold position save failed: \(error)")
}
}
}
}