跪拜 Guibai
← All articles
Frontend · Artificial Intelligence · GitHub

Fund Helper Puts Portfolio Tracking into Your Browser, Editor, and Desktop

By 八号当铺 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Multi-platform delivery is a hard engineering problem that forces teams to choose between a shared backend and independent clients. Fund Helper demonstrates a pragmatic middle path: a unified business model with per-platform runtimes, where normalization rules are duplicated across Python, TypeScript, Rust, and Kotlin to keep portfolio numbers identical everywhere without a mandatory server dependency.

Summary

Fund Helper aggregates fund holdings from the Chinese platform Yangjibao into a unified snapshot, then distributes that snapshot across five distinct clients. A FastAPI backend with MongoDB powers the full web dashboard, while the browser extension, editor plugins, and Tauri desktop client operate independently by connecting directly to the upstream API. Each client normalizes raw fund NAV fields using a shared priority rule so that estimated change percentages and daily returns stay consistent whether viewed in a Chrome popup, a VS Code sidebar, or a macOS menu bar. The project also bundles market rankings from AKShare/East Money, sector heatmaps, and configurable push notifications to DingTalk, Feishu, and WeCom. Docker Compose, GitHub Actions release scripts, and an Rspress documentation site turn the codebase into distributable software rather than a personal script.

Takeaways
Fund Helper surfaces fund holdings, daily returns, market rankings, sector heatmaps, and push notifications across five clients: web, Chrome extension, VS Code/Cursor, JetBrains, and Tauri desktop.
The web app uses a FastAPI BFF to hide Yangjibao's MD5-based request signing and to merge upstream data into a single PortfolioSnapshot that the React frontend consumes.
The Chrome extension runs fully client-side with no backend, performing signing, normalization, and snapshot assembly in TypeScript and persisting tokens in chrome.storage.local.
The Tauri desktop client delegates all network and state to Rust commands invoked from React; SQLite stores tokens and notification config, while a system tray and macOS menu bar show live returns.
Editor plugins keep Webview as a display-only layer: the VS Code Extension Host and JetBrains Kotlin host handle API calls, state caching, and broadcasting a single lastSnapshot to sidebars, panels, and status bars.
Fund NAV fields are normalized with a fallback chain (gszzl → zsgzzl → vgszzl for estimated change) that is replicated in Python, TypeScript, Rust, and Kotlin to prevent cross-client return mismatches.
Account-level return curves require requesting by an array of account IDs; a single account_id often returns only the aggregate curve.
Notifications reuse the same snapshot pipeline: before pushing to DingTalk, Feishu, or WeCom, the system pulls a fresh snapshot and renders it through channel-specific templates.
Docker Compose bundles the FastAPI app with MongoDB for one-command deployment; GitHub Actions build and publish the Chrome extension, VS Code extension, JetBrains plugin, and desktop releases.
Conclusions

Duplicating business logic across four languages is normally an anti-pattern, but here it is a deliberate trade-off that keeps the browser extension and desktop client zero-dependency, which lowers adoption friction more than a shared library would.

The decision to use a hand-rolled SVG chart instead of ECharts is driven by bundle-size constraints in Chrome extension popups and VS Code Webviews, where every kilobyte affects perceived startup speed.

QR-code login across Webview, JCEF, and Tauri surfaces a subtle state-machine problem: timer cleanup and session deduplication become the hardest parts, not the OAuth flow itself.

Multi-entry snapshot broadcasting in editor plugins solves a user-trust issue, not a performance one. When the status bar, bottom panel, and sidebar show different numbers, users assume the tool is broken.

Concepts & terms
BFF (Backend for Frontend)
An intermediate server layer that aggregates, transforms, and secures data from multiple upstream services so that each frontend client receives a single, ready-to-consume payload. In Fund Helper, the FastAPI BFF hides Yangjibao's request signing and merges fund, index, and market data into a PortfolioSnapshot.
PortfolioSnapshot
A normalized data structure containing total assets, daily return, per-account fund lists, index quotes, and trading-session flags. Every Fund Helper client assembles or receives this same structure to ensure consistent display across platforms.
Tauri
A framework for building desktop applications using a Rust backend and a web-technology frontend. Fund Helper's desktop client uses Tauri v2 to invoke Rust commands from React, with SQLite for local persistence and native system tray integration.
CRXJS
A build tool that enables using Vite and React to develop Chrome extensions with Manifest V3. Fund Helper's browser extension relies on it to bundle the popup UI and manage the extension lifecycle.
JCEF (Java Chromium Embedded Framework)
A Java wrapper around the Chromium Embedded Framework that allows embedding a full browser engine inside Java applications. JetBrains IDEs use JCEF to host Webview-based tool windows, which Fund Helper's Kotlin plugin leverages for its React UI.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗