# wlftally **Repository Path**: waisaa/wlftally ## Basic Information - **Project Name**: wlftally - **Description**: 日常消费记账软件 - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-17 - **Last Updated**: 2026-09-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # wlftally 陟金录 — 家庭记账应用 基于 wlfapp 通用模板构建的记账应用,从旧项目 wlftallybk 重构而来。包含完整的用户认证、家庭多租户、版本更新、文件上传、反馈/捐赠等基础设施,业务侧提供账本、记账、分类、统计报表、xlsx 导入导出能力。 - **线上地址**:https://tally.waisaa.com - **后端端口**:8002 - **App 标识**:`__UNI__559F11F` --- ## 目录结构 ``` apps/wlftally/ ├── back/ # FastAPI 后端 │ ├── app/ │ │ ├── main.py # 应用入口,挂载路由、中间件、异常处理;启动时预置分类 │ │ ├── config.py # pydantic-settings 配置类,读取 .env.* │ │ ├── database.py # 异步引擎 + 会话工厂 + Base 声明基类 │ │ ├── models.py # SQLAlchemy 模型(通用 + 陟金录业务,见下) │ │ ├── api/ # 路由层(薄控制器) │ │ │ ├── auth.py # 登录/注册/登出/刷新/重置密码/修改密码 │ │ │ ├── user.py # 个人资料 CRUD + 注销 │ │ │ ├── family.py # 家庭 CRUD/加入/退出/切换/邀请码/成员管理 │ │ │ ├── settings.py # 上传、邮箱验证码、反馈、捐赠、数据导入导出 │ │ │ ├── versions.py # 版本检查 + APK 上传 │ │ │ ├── ledger.py # 账本 CRUD/切换/成员/邀请码(家庭多租户) │ │ │ ├── transaction.py # 记账增删改查/分组汇总/分组明细/分类树 │ │ │ └── statistics.py # 仪表盘统计 + 收支报表 │ │ ├── services/ # 业务逻辑层 │ │ │ ├── auth.py / deps.py / email.py / family.py / settings.py # 模板通用 │ │ │ ├── ledger.py # 账本业务逻辑 │ │ │ ├── transaction.py # 记账业务逻辑 │ │ │ ├── statistics.py # 统计计算 │ │ │ ├── seed.py # 预置两级分类数据 │ │ │ └── tally_io.py # xlsx 导入/导出/模板下载 │ │ ├── schemas/ # Pydantic 请求/响应模型(9 个模块) │ │ ├── core/ # limiter(slowapi)+ upload(UploadRegistry) │ │ └── utils/ # wlf 前缀通用工具(wlfres/wlflog/wlfcache/...) │ ├── alembic/ # 数据库迁移(异步) │ ├── scripts/ │ │ ├── check_contract.py # 前后端 API 路径契约校验 │ │ └── gen_frontend_types.py # 从 OpenAPI 生成前端 TypeScript 类型 │ ├── .env.dev / .env.prod # 环境变量 │ ├── Dockerfile # Python 3.12-slim + uv,端口 8002 │ ├── docker-compose*.yml # 容器编排(wlftally 服务) │ ├── pyproject.toml # Python 依赖声明(uv 管理,唯一来源) │ └── uv.lock # 锁定依赖版本(自动生成,勿手改) │ ├── face_uni/ # UniApp + Vue3 前端 │ ├── config/ # 全局配置(禁止在页面中硬编码) │ │ ├── env.js # 环境检测(develop→localhost:8002,release→线上) │ │ ├── api.js # 后端接口路径(ApiPath 对象) │ │ ├── brand.js / router.js / tabbar.js / storage.js / code.js / dict.js / upload.js │ ├── components/ # wlf- 前缀通用组件(easycom 自动注册) │ ├── pages/ │ │ ├── index/index.vue # 首页(仪表盘) │ │ ├── records/records.vue # 流水(内嵌 wlf-report-view 报表) │ │ ├── addRecord/ # 记一笔(中间 FAB) │ │ ├── assets/assets.vue # 资产 │ │ ├── bookManage/ # 账本管理 │ │ ├── settings/ # 我的/个人资料/家庭管理/数据导入导出 │ │ ├── login/ # 登录/注册/重置密码 │ │ └── common/webview.vue # 通用 WebView 容器 │ ├── api/ # 接口封装(按模块拆分) │ ├── utils/ # wlfreq/wlfinterceptor/wlfdate/wlfeventbus/wlfappupdate │ ├── pages.json / manifest.json / uni.scss │ └── ... │ ├── resources/ # 项目素材 └── README.md # 本文件 ``` --- ## 数据模型速览 通用(模板):`User` / `Family` / `UserFamily` / `Feedback` / `Donation` / `Version` 业务(陟金录): - `Ledger` 账本:挂在家庭下(`family_id`),家庭成员自动可见;保留 `LedgerMember` + 邀请码兼容旧共享账本数据 - `Transaction` 记账记录:两级分类(`category_id`/`subcategory_id`)、账户枚举(支付宝/微信钱包/银行卡) - `Category` 分类:自关联两级,启动时由 `services/seed.py` 预置 - 注意:`Transaction` 有 `(user_id, transaction_date, subcategory_id)` 唯一约束(旧设计沿袭,有意保留) --- ## 技术栈 **后端**:FastAPI + SQLAlchemy 2.0(异步)+ asyncpg + Alembic + pydantic-settings,Python 3.12 **前端**:uni-app + Vue 3 + uv-ui + sass,HBuilderX 开发调试 **部署**:Docker Compose + Nginx 反向代理(tally.waisaa.com)+ PostgreSQL(库名 wlftally) --- ## 快速启动 ### 后端 依赖管理使用 [uv](https://docs.astral.sh/uv/)(先安装:`powershell -c "irm https://astral.sh/uv/install.ps1 | iex"`): ```bash cd apps/wlftally/back uv sync # 自动创建 .venv 并按 uv.lock 安装依赖 uv run python -m app.main # 访问 http://localhost:8002/docs 查看 API 文档(仅 ENV=dev) ``` 依赖变更用 `uv add <包名>` / `uv remove <包名>`(自动更新 pyproject.toml + uv.lock),不要手工编辑 uv.lock。 ### 前端 用 HBuilderX 打开 `face_uni/` 目录,选择运行到浏览器/手机即可。develop 环境 API 指向 `http://localhost:8002/api`,需先启动本地后端。 ### 契约校验 ```bash cd apps/wlftally/back python scripts/check_contract.py # 比对后端路由与前端 config/api.js python scripts/gen_frontend_types.py # 重新生成 face_uni/types/api.d.ts ``` ### App 版本发布(整包 + 热更新) 更新决策由服务端集中完成:客户端 `POST /api/versions/check` 上报 `version_code`(原生壳)+ `version_name`(资源版本),服务端按优先级返回 `apk`(整包)/ `wgt`(热更)/ `none`;旧接口 `/api/versions/latest` 只返回 APK 记录,旧客户端无感。 **版本号约定**:versionName 用三段语义化——前两位(major.minor)变化 = APK 整包发版(如 1.2.0 → 1.3.0),第三位(patch)变化 = wgt 热更发版(如 1.2.0 → 1.2.1);**versionCode 无论哪种发版都必须 +1**(runtime 靠它判断资源是否高于基座,只升 name 不升 code 会导致安装重启后回退旧版)。服务端决策在同类型记录中按 versionName 取最高者下发,作废的 wgt 记录要及时删除。 ```bash # 1. 整包(APK):HBuilderX 云打包 → 上传 APK 到服务器 /srv/waisaa/storage/public/downloads/ docker exec -it wlftally python scripts/add_version.py -m "更新日志" # 2. 热更新(wgt):HBuilderX 发行 → 制作应用wgt包(manifest 的 versionName 与 versionCode 都必须升, # runtime 据此判断资源是否高于基座,只升 name 不升 code 会导致安装重启后回退旧版) # → 上传 wgt 到同一目录 → 登记 docker exec -it wlftally python scripts/add_version.py --type wgt -m "更新日志" # wgt 依赖某原生底包能力时,限制只发给该底包及以上的用户 docker exec -it wlftally python scripts/add_version.py --type wgt -m "更新日志" --target-code 16 ``` **wgt 禁忌(发热更前必须人工确认)**:改动 `manifest.json`(权限/模块/原生插件/splashscreen 等)、新增原生依赖的版本只能发 APK,不允许发 wgt;wgt 仅限纯前端改动(页面/样式/JS 逻辑)。热更包出问题时,发一个 versionName/versionCode 都更高的"回滚 wgt"(内容为上一版资源),或引导用户安装新 APK。 **离线打包基座前置条件(2026-09 踩坑实锤)**:5+ SDK ≥1.9.9.82650 的 `plus.runtime.install` 解压 wgt 依赖 zip4j,但官方离线 SDK 工程未附带该依赖。基座缺 zip4j 时 install 线程崩溃(logcat 特征:`Unresolved exception class when finding catch block: net.lingala.zip4j.exception.ZipException` + `NullPointerException: Attempt to read from null array at io.dcloud.feature.pdr.RuntimeFeatureImpl$a.run`),wgt 安装中断回退旧版,造成"下载→闪弹窗→重启→回退→重复推送"死循环。修复:离线工程 `simpleDemo/build.gradle` 的 dependencies 加 `implementation 'net.lingala.zip4j:zip4j:2.11.5'`,**必须重打 APK 整包发版**(wgt 无法补原生类)。升级 SDK 版本后需重新确认该依赖仍存在。 --- ## 端口分配 | 项目 | 端口 | 域名 | |------|------|------| | wlfapp(模板) | 8001 | - | | **wlftally(本项目)** | **8002** | tally.waisaa.com | | wlfrecipe | 8003 | recipe.waisaa.com |