← 交接首页 工具首页 财富管理系统 · Project Handoff
Doodle 子系统 · 孩子(coco)的真实财富记账与计算工具

财富管理系统 — 项目交接

目标:给孩子(Cordelia / coco)建立一套记录真实财富的系统,锻炼财务管理能力——知道自己有多少钱、 钱从哪来花到哪去、借钱有成本、投资有风险和收益。doodle 只负责记录/计算/展示,不碰真实资金转移 (银行转账、股票下单仍由家长在真实世界完成)。与积分系统(services/rewards.py) 完全独立、互不兑换。需求文档是仓库根目录的 WEALTH_SPEC.md / WEALTH_IMPL.md, 开发顺序按 SPEC §5 的 6 个优先级走,本次 session(2026-08-25)一天内全部做完。

数据模型 + 现金收支 + 余额总览 汇率折算(双币种展示) 家庭借贷(自动计息) 投资 - 家庭项目 投资 - 美股 每日快照 + 趋势(折线图数据源) 前端页面未做(目前只有 API) yfinance 在本 VPS 被限流,需生产环境实测

已做 / 未做

✅ 已完成(全部用真实 HTTP 请求 + 手算数值核对过)

  • 数据模型storage/models.py 里 8 张 Wealth* 表,一次迁移(tools/migrate_v22.py)全部建好,覆盖全部 6 个优先级的表结构,避免以后再补迁移。
  • 现金收支:孩子自己记收入(压岁钱/劳动所得)/支出,GET/POST /transactionsDELETE /transactions/{id}
  • 余额总览GET /summaryWEALTH_IMPL.md §3 结构返回,现金 + 借贷净值 + 投资市值,CNY/THB 双币种同时展示。
  • 汇率折算services/wealth_fx.py,按日缓存(open.er-api.com),命中当天缓存不重复调接口,接口失败兜底最近一次成功缓存;从只服务 CNY/THB 泛化到支持 USD(美股要用)。
  • 家庭借贷GET/POST /loansPOST /loans/{id}/settle,应计利息实时按天数算(单利 Actual/365),不依赖定时任务;已结清的贷款冻结在结清时点。
  • 投资-家庭项目POST /investments/family_project,收益按约定 end_date 到期封顶(不论是否手动结算),本金×(1+年化×天数/365)。
  • 投资-美股POST /investments/us_stockservices/wealth_stock.py 接 yfinance,按 (ticker, 日期) 缓存 + 失败兜底最近缓存;卖出走 sell_price 冻结。
  • 每日快照 + 趋势services/wealth_accrual.pytools/wealth_daily_accrual.py 定时调用)每天落 wealth_loan_accruals/wealth_investment_valuations/wealth_daily_snapshots 三张历史表,幂等;GET /trend?days=30 读快照给折线图用。
  • crontab:新增 1 条(曼谷时间 21:00 跑每日计息/快照),已按"VPS 系统时区是东京不是曼谷"的规则换算成 0 23 * * *

🚫 被阻塞 / 没做

  • 前端页面:目前只有后端 API,孩子和家长都还看不到界面。WEALTH_IMPL.md §4 提到要加柱状图(读 /summary.breakdown)和折线图(读 /trend),复用现有前端已引入的图表库——需要单独开一个 session 做。
  • yfinance 行情在本 VPS 被限流:开发环境(腾讯云 IP)反复实测均遇到 YFRateLimitError,代码本身的缓存/兜底逻辑已用预置数据验证正确,但没有一次成功的真实行情请求。生产环境部署后需要实测一次是否同样被限流,如果是,需要评估换数据源(付费 API 或加代理)。
  • owner 硬编码 "coco"tools/wealth_daily_accrual.py 只处理一个孩子账号,若后续 cordelia(用户名待定)也要用,需要改成从 users 表按角色查询。
  • 快照不支持精确历史补算wealth_ledger.loan_net_cny/investment_value_cny 内部按"今天"实时算,run_daily_accrualtarget_date 只对明细表(计息/估值)生效,快照汇总值本质是"跑任务那一刻"的状态——这是有意的简化(同 harrow_task_rollover.py 的既有约定),不是 bug。

环境信息

线上入口暂无(前端未做)
后端路由routers/wealth.py,前缀 /doodle/api/wealth,main.py 已挂载
核心服务模块services/wealth_ledger.py(现金/借贷/投资的增删改查 + 余额计算)、wealth_fx.py(汇率)、wealth_stock.py(美股行情)、wealth_accrual.py(每日计息/折算/快照)
数据表storage/models.py 里所有 Wealth* 开头的类;迁移脚本 tools/migrate_v22.py(一次建好全部 8 张表)
依赖requirements.txt 新增 yfinance==1.6.0
owner 约定现金收支/趋势查看:owner=当前登录账号(孩子自查自记);借贷/投资:owner 由调用方显式传入("家长发起",操作者可能是 dad 账号)

API 一览

GET    /doodle/api/wealth/summary                     总资产总览(双币种折算)
GET    /doodle/api/wealth/trend?days=30                总资产历史趋势
GET    /doodle/api/wealth/transactions                 收支流水
POST   /doodle/api/wealth/transactions                 记一笔收入/支出
DELETE /doodle/api/wealth/transactions/{id}
GET    /doodle/api/wealth/loans                        借贷列表(含实时应计利息)
POST   /doodle/api/wealth/loans                        发起借贷协议
POST   /doodle/api/wealth/loans/{id}/settle            结清借贷
GET    /doodle/api/wealth/investments                  投资列表(家庭项目+美股)
POST   /doodle/api/wealth/investments/family_project    发起家庭项目
POST   /doodle/api/wealth/investments/us_stock          买入美股
POST   /doodle/api/wealth/investments/{id}/settle       结算/卖出投资

crontab(本次新增 1 条)

0 23 * * *   wealth_daily_accrual.py   # 曼谷 21:00(已按东京+2h 换算)计息/折算/生成快照

常用命令

cd /home/ubuntu/doodle
git status --short
pm2 restart doodle && pm2 logs doodle --lines 20 --nostream

# 查某个孩子的总资产
curl -s "http://127.0.0.1:8090/doodle/api/wealth/summary" -H "Cookie: doodle_session=..."

# 手动补跑一次每日计息/快照
.venv/bin/python tools/wealth_daily_accrual.py

# 查快照历史
sqlite3 data/doodle.db "SELECT snapshot_date, total_cny, total_thb FROM wealth_daily_snapshots ORDER BY snapshot_date DESC LIMIT 10;"

已知坑(详见 LESSONS.md 的 "财富管理模块" 章节)

yfinance 被限流Yahoo Finance 非官方接口,对部分云服务商 IP 段(本机是腾讯云)直接限流/拒绝,不是偶发抖动,反复重试/换 ticker 都复现。已做缓存 + 兜底最近成功价格,生产环境需实测确认是否同样受限。
汇率表泛化services/wealth_fx.py 原本只服务 CNY/THB,接入美股后加了 quote_currency 参数(默认 THB,旧调用点不用改),一次接口调用顺手把 THB 和 USD 都缓存,不多打请求。
VPS 系统时区是东京不是曼谷(重要,同 Harrow 那次踩的坑)系统时区 Asia/Tokyo(UTC+9),应用逻辑用 Asia/Bangkok(UTC+7),差 2 小时,且这个 cron 版本不支持 CRON_TZ。本模块的 crontab 一开始也写错了(0 21 * * *,以为是曼谷 21:00,实际是曼谷 19:00),同日随 Harrow 那次时区排查一起发现并修正为 0 23 * * *新增任何要求"曼谷几点触发"的定点任务,先 timedatectl 确认系统时区再换算
家庭项目到期封顶用 end_date 而非结算时间戳WealthInvestment 表没有类似 WealthLoan.settled_at 的实际结算时间字段,家庭项目收益按约定 end_date 封顶(min(end_date, today)),不论是否已手动点结算,到期后金额就不再继续增长——这样更贴合"收益按合同条款算"的语义。

下一步建议

  1. 前端页面:先确认复用哪个现有图表库(看 Deficit 或 Harrow 页面已经引入的是什么),做一个总览页(余额卡片 + 柱状图)+ 记账表单 + 趋势折线图。
  2. 生产环境实测 yfinance:部署后跑一次 tools/wealth_daily_accrual.py,看美股持仓能不能拿到真实行情;如果还是限流,评估付费数据源。
  3. 观察真实使用一段时间后,看 WEALTH_SPEC.md §6 里的"临时默认规则"(每日结算时点、汇率更新频率)是否需要调整。
本页对应 2026-08-25 的一次 session(从 WEALTH_SPEC.md/WEALTH_IMPL.md 起步,按优先级 1→6 一路做完数据模型、现金收支、汇率、借贷、家庭项目投资、美股投资、每日快照/趋势,全部部署验证)。 详细过程记录在仓库根目录 CODING-LOG.md(搜"财富管理系统")和 LESSONS.md