对照 ledgerly 自文档上次更新以来的全部改动,补充新功能文档、修正品牌与版本残留,并新增下载与安装页;配图已重新截取并上传 OSS,本地截图目录不纳入版本管理。 变更内容: - 新增「智能查数」「账单关系」两个使用文档页,并登记侧边栏与总览 - 设置页扩充为八个面板(账号安全、数据库实时进度、外观、导航等)并改为即时保存 - 收支记录补充关系列、批量操作、AI 搜索浮窗、可配置分页与「账单详情」弹窗 - 统计面板补充饼图/渠道图下钻与筛选双向联动;规则管理补充批量删除 - AI 能力补充智能查数与关系命名;新增下载与安装页 - 开发文档同步:AI 13 类调用、数据模型 24 张表、API、前端偏好、构建部署 - 统一「汇账 Ledgerly」品牌与「设置/日志」菜单文案,版本号对齐 0.1 - 截图脚本新增账单详情等场景并更新登记 说明: - download.md 的 OSS 下载链接保持待发布产物回填,暂不调整 - 已通过 npm run docs:build 与 npm run typecheck:scripts
10 KiB
数据模型
系统共 24 张表,覆盖用户、记账、账单关系、渠道、标签、规则、AI、通知、邮箱账单与日志审计。所有表主键为自增 id,表间关系均为逻辑外键(通过 user_id、category_id 等字段在应用层维护,未声明物理外键)。
表清单
| 业务域 | 表名 | 说明 |
|---|---|---|
| 用户 | t_user |
用户表(用户名、BCrypt 密码、昵称) |
| 用户 | t_user_preference |
每用户多条的用户偏好(键值) |
| 记账 | t_category |
收支分类(收入/支出,含图标与排序) |
| 记账 | t_transaction |
收支记录(核心表) |
| 记账 | t_channel |
用户自定义交易渠道(内置渠道由枚举维护) |
| 标签 | t_tag |
标签 |
| 标签 | t_transaction_tag |
交易-标签关联(多对多) |
| 账单关系 | t_transaction_group |
账单关系组(父子/同级) |
| 账单关系 | t_transaction_group_item |
关系组成员(角色与顺序) |
| 规则 | t_rule |
自定义归类规则 |
| 规则 | t_rule_tag |
规则-标签关联(多对多) |
| AI | t_ai_config |
每用户一条的 AI 配置(含视觉模型) |
| AI | t_ai_usage |
AI 调用用量记录 |
| AI | t_ai_image |
图片记账上传的截图及其解析摘要 |
| AI | t_ai_image_transaction |
图片与解析生成交易的关联 |
| AI | t_ai_sql_history |
智能查数提问历史(每用户多条) |
| 通知 | t_notification_config |
每用户一条的提醒配置 |
| 通知 | t_notification_channel |
通知渠道 |
| 通知 | t_notification_log |
通知发送日志 |
| 邮箱账单 | t_email_bill_config |
IMAP 邮箱配置 |
| 邮箱账单 | t_email_bill_sender_rule |
发件人规则 |
| 邮箱账单 | t_email_bill_message |
拉取到的账单消息 |
| 日志 | t_http_log |
接口访问日志 |
| 日志 | t_login_log |
登录日志 |
关系概览
以用户为中心(一对多)
| 主体 | 关系 | 对象 | 关联字段 |
|---|---|---|---|
| t_user | 1 : N | t_category | user_id |
| t_user | 1 : N | t_transaction | user_id |
| t_user | 1 : N | t_tag | user_id |
| t_user | 1 : N | t_channel | user_id |
| t_user | 1 : N | t_transaction_group | user_id |
| t_user | 1 : N | t_rule | user_id |
| t_user | 1 : N | t_ai_image | user_id |
| t_user | 1 : N | t_notification_channel | user_id |
| t_user | 1 : N | t_email_bill_config | user_id |
| t_user | 1 : N | t_email_bill_sender_rule | user_id |
| t_user | 1 : N | t_email_bill_message | user_id |
| t_user | 1 : N | t_user_preference | user_id |
| t_user | 1 : N | t_ai_usage / t_http_log / t_login_log / t_notification_log | user_id |
一对一(每用户一条)
| 主体 | 关系 | 对象 | 关联字段 |
|---|---|---|---|
| t_user | 1 : 1 | t_ai_config | user_id(唯一) |
| t_user | 1 : 1 | t_notification_config | user_id(唯一) |
记账与标签
| 主体 | 关系 | 对象 | 关联字段 / 中间表 |
|---|---|---|---|
| t_category | 1 : N | t_transaction | transaction.category_id(可空=未分类) |
| t_transaction | N : M | t_tag | 中间表 t_transaction_tag(联合唯一) |
| t_category | 1 : N | t_rule | rule.category_id(命中目标分类) |
| t_rule | N : M | t_tag | 中间表 t_rule_tag(联合唯一) |
| t_ai_image | 1 : N | t_ai_image_transaction | image_id |
| t_transaction | 1 : N | t_ai_image_transaction | transaction_id |
| t_transaction_group | 1 : N | t_transaction_group_item | group_id(联合唯一 group_id + transaction_id) |
| t_transaction | 1 : N | t_transaction_group_item | transaction_id |
交易记录通过
channel编码关联渠道:内置渠道编码取自ChannelType枚举,自定义渠道编码取自t_channel.code(用户内唯一),因此渠道改名不影响历史记录。
通知与邮箱账单
| 主体 | 关系 | 对象 | 关联字段 |
|---|---|---|---|
| t_notification_channel | 1 : N | t_notification_log | channel_id(可空) |
| t_email_bill_config | 1 : N | t_email_bill_message | config_id |
| t_email_bill_sender_rule | 1 : N | t_email_bill_message | sender_rule_id(可空) |
邮箱配置与发件人规则的默认分类通过
expense_category_id/income_category_id逻辑引用t_category;默认标签以 JSON 数组字符串保存(未用关联表)。
核心表字段
t_transaction(收支记录)
| 字段 | 说明 |
|---|---|
| user_id | 所属用户 |
| category_id | 分类(可空,未分类) |
| type | INCOME / EXPENSE |
| amount | 金额(DECIMAL(12,2)) |
| channel | 交易渠道(如 ALIPAY / WECHAT / CASH / BANK) |
| remark | 备注 |
| remark_hidden | 备注是否隐藏 |
| external_no | 外部账单交易号,导入去重 |
| transaction_time | 交易发生时间 |
| include_in_stats | 是否计入统计(默认 1) |
| raw_data | 原始字段 JSON(账单导入的原始账单行,或图片记账由模型提取的原始字段;手动记录为空) |
关键索引:唯一索引 uk_user_external (user_id, external_no) 实现导入去重;idx_user_time (user_id, transaction_time) 支撑按时间查询与统计。
t_category / t_tag
- 分类:
name、type、icon(Emoji)、sort_order。 - 标签:
name、color、sort_order,唯一索引uk_user_name (user_id, name)保证用户内标签名唯一。
t_rule
name、enabled、sort_order(优先级)、type、keyword、min_amount、max_amount、category_id。
t_transaction_group / t_transaction_group_item(账单关系)
t_transaction_group:user_id、type(PARENT_CHILD父子 /SIBLING同级)、name、remark;索引user_id。t_transaction_group_item:group_id、transaction_id、role(PARENT/CHILD/MEMBER)、sort_order(同级链路的顺序);唯一索引(group_id, transaction_id)防止同一账单在同一组内重复,另有transaction_id索引。- 约束:一个关系组至少两条成员、上限 50 条;父子关系必须且只能有一条
PARENT;一笔账单不能同时加入两个同类型的关系组。删除账单时会自动清理其成员记录,成员不足两条或父子组缺父时整组删除(仅解除关联,不删除账单)。详见账单关系(使用说明)。
t_channel(自定义交易渠道)
| 字段 | 说明 |
|---|---|
| user_id | 所属用户 |
| code | 渠道编码,后端生成(CUSTOM_ + 随机串),稳定不变,交易记录按此关联 |
| name | 渠道名称,用户可修改 |
| sort_order | 排序序号,数值越小越靠前 |
唯一索引 uk_channel_user_code (user_id, code)、uk_channel_user_name (user_id, name) 分别保证用户内编码与名称唯一。仅存放用户自建渠道,内置渠道由 ChannelType 枚举维护。
t_ai_config / t_ai_image
t_ai_config:每用户一条,含enabled、api_key、base_url、model与vision_model(图片记账使用的视觉模型,默认deepseek-flash)。t_ai_image:图片记账上传的截图,一次上传可产生多条(最多 5 张),含user_id、file_path(相对应用运行目录,落盘于data/ai-images/)、mime_type、file_size、width、height、model(解析所用视觉模型)、summary(识别摘要)、record_count(该图识别出的记录数)。t_ai_image_transaction:图片与交易记录的关联,含image_id、transaction_id,唯一索引uk_ai_image_transaction (image_id, transaction_id);每笔记录按模型标注的来源图分别建立关联。t_ai_sql_history:智能查数(自然语言转 SQL)的提问历史,含user_id、query_text(截断 500 字符)、created_at;索引(user_id, created_at)。
去重键:图片记账与批量导入使用「收支类型 + 金额 + 交易时间(到秒)」作为业务去重键(
TransactionDedupUtils),用于标记疑似重复并在导入时跳过,弥补这类记录没有外部交易号的不足。
t_user_preference
按用户存储的键值偏好:pref_key 为偏好键,pref_value 为标量字符串或 JSON。已使用的偏好键包括:billSourceIncludeInStats(各来源计入统计默认值)、excludedDisplayStyle(不计入记录展示样式)、showHiddenData(是否显示已隐藏备注的记录)、paginationPrefs(表格分页候选与默认值)、navMenu(左侧导航顺序与显隐)。唯一索引 uk_user_pref (user_id, pref_key) 保证每个用户每个偏好仅一条。未配置时由应用回退系统默认值。
t_email_bill_config
IMAP 连接信息(host/port/use_ssl/username/password/folder)、状态(status/last_error/last_connected_at/last_uid)、默认归类(expense_category_id/income_category_id/tag_ids)与拉取范围(fetch_scope/fetch_limit)。
t_email_bill_message
config_id、uid、message_id、subject、from_address、received_at、附件信息、命中的 sender_rule_id 与 bill_type、状态(PULLED/CONFIRMED/IGNORED)、error。唯一索引 uk_email_bill_message (config_id, uid, attachment_index) 防止重复拉取。
MySQL 与 SQLite 双脚本
系统同时维护 sql/init.sql(MySQL)与 sql/schema-sqlite.sql(SQLite),表结构与语义保持一致,主要差异:
| 维度 | MySQL | SQLite |
|---|---|---|
| 主键自增 | BIGINT AUTO_INCREMENT |
INTEGER PRIMARY KEY AUTOINCREMENT |
| 布尔 | TINYINT(1) |
INTEGER |
| 金额 | DECIMAL(12,2) |
NUMERIC(12,2) |
| JSON | JSON |
TEXT |
| 时间默认值 | DEFAULT CURRENT_TIMESTAMP |
strftime(...,'localtime') |
updated_at 自动更新 |
ON UPDATE CURRENT_TIMESTAMP |
AFTER UPDATE 触发器 |
| 语句分隔符 | ; |
;;(兼容触发器内部的分号) |
| 列注释 | 支持 | 不支持 |
统计模块会依据当前数据源类型,切换 MySQL 与 SQLite 的时间分组函数(DATE_FORMAT/QUARTER 与 strftime)。
初始化与迁移
- 应用启动时由
SchemaInitializer执行对应建表脚本(幂等)。 - 建表脚本只做
CREATE TABLE IF NOT EXISTS,无法修改已有库的列;需要就地变更列名或新增列时由SchemaMigrator在启动/切换数据源时幂等迁移(如t_transaction.payment_method→channel改名、t_ai_config.vision_model加列;MySQL 用information_schema、SQLite 用PRAGMA table_info检测列是否存在)。 - 新用户注册时由
DefaultDataInitializer初始化默认分类、标签与邮箱发件人规则。