docs: 同步汇账最新功能,新增智能查数、账单关系等文档
对照 ledgerly 自文档上次更新以来的全部改动,补充新功能文档、修正品牌与版本残留,并新增下载与安装页;配图已重新截取并上传 OSS,本地截图目录不纳入版本管理。 变更内容: - 新增「智能查数」「账单关系」两个使用文档页,并登记侧边栏与总览 - 设置页扩充为八个面板(账号安全、数据库实时进度、外观、导航等)并改为即时保存 - 收支记录补充关系列、批量操作、AI 搜索浮窗、可配置分页与「账单详情」弹窗 - 统计面板补充饼图/渠道图下钻与筛选双向联动;规则管理补充批量删除 - AI 能力补充智能查数与关系命名;新增下载与安装页 - 开发文档同步:AI 13 类调用、数据模型 24 张表、API、前端偏好、构建部署 - 统一「汇账 Ledgerly」品牌与「设置/日志」菜单文案,版本号对齐 0.1 - 截图脚本新增账单详情等场景并更新登记 说明: - download.md 的 OSS 下载链接保持待发布产物回填,暂不调整 - 已通过 npm run docs:build 与 npm run typecheck:scripts
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
|
||||
## 客户端
|
||||
|
||||
`DeepSeekClient` 基于 **OpenAI 官方 Java SDK**(`com.openai:openai-java`)调用,通过自定义 `baseUrl` 指向 DeepSeek 的 OpenAI 兼容接口:
|
||||
`DeepSeekClient` 直接调用 DeepSeek 的 **OpenAI 兼容 Chat Completions 接口**(`{baseUrl}/chat/completions`),为减小可执行 jar 体积,使用 **Spring `RestClient` + JDK `HttpClient`** 而非第三方 SDK:
|
||||
|
||||
- 使用 `OpenAIOkHttpClient`,按 `(baseUrl, apiKey, 超时)` 缓存客户端(上限 32,超出后清空重建)。
|
||||
- `temperature = 0.2`,`response_format = {"type":"json_object"}`,非流式,`maxRetries(0)`;文本请求超时 **120 秒**,视觉请求超时 **240 秒**(`chatWithImages` 单独放宽)。
|
||||
- 取 `choices[0].message.content` 与 `usage`(输入/输出/总 token;缓存命中 token 取 `promptTokensDetails.cachedTokens`,DeepSeek 私有字段读不到时为 0)。
|
||||
- 请求体包含 `model`、`messages`、`temperature = 0.2`、`response_format = {"type":"json_object"}`、`stream = false`。
|
||||
- 文本请求超时 **120 秒**,视觉请求超时 **240 秒**(`chatWithImages` 单独放宽)。
|
||||
- 解析 `choices[0].message.content` 与 `usage`(输入/输出/总 token;缓存命中 token 取 DeepSeek 的缓存字段,读不到时为 0)。
|
||||
- 对外保持 `chat(baseUrl, apiKey, model, systemPrompt, userPrompt)` 与 `DeepSeekResponse` 不变,各 `Ai*ServiceImpl` 无需改动。
|
||||
- 另有 `chatWithImages(baseUrl, apiKey, model, systemPrompt, userText, imageDataUrls)`:将一张或多张图片以 base64 data URL 作为 `user` 消息的内容块发送(DeepSeek 视觉接口要求图片只能出现在 user 消息中),供图片记账使用。
|
||||
- SDK 异常统一转 `BusinessException`;异常链含超时(`SocketTimeoutException` 等)时返回面向用户的友好提示。
|
||||
- 调用异常统一转 `BusinessException`;异常链含超时(`SocketTimeoutException` 等)时返回面向用户的友好提示。
|
||||
|
||||
配置存储于 `t_ai_config`(每用户一条):`enabled`、`api_key`、`base_url`(默认 `https://api.deepseek.com`)、`model`(默认 `deepseek-chat`)、`vision_model`(图片记账视觉模型,默认 `deepseek-flash`)。API Key 对外脱敏,支持连通性测试。
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
|
||||
- **禁止**:`rawData`(账单原始 JSON)、金额、交易号、账户/卡号、姓名、地址、电话等。
|
||||
- **允许**:收支类型、交易渠道、备注(必要时截断/脱敏)、分类与标签的名称/ID、用户主动输入的自然语言文本。
|
||||
- **智能查数**:仅发送用户提问与可查询的**表结构说明**,不发送任何账单数据行;数据查询与隔离在本地由安全网关完成。
|
||||
- **图片记账例外**:用户主动上传的账单/支付截图会**整张**发送给视觉模型(可能含金额、交易对方、订单号等),视为用户主动提供的输入;图片同时落盘于 `data/ai-images/`,仅本人可经 `GET /api/ai/image/{id}` 访问,禁止用于其他外发场景。
|
||||
|
||||
## 调用类型
|
||||
|
||||
`AiCallType` 定义了 11 类调用,全部记录用量:
|
||||
`AiCallType` 定义了 13 类调用,全部记录用量:
|
||||
|
||||
| 枚举值 | 含义 |
|
||||
|---|---|
|
||||
@@ -40,6 +41,8 @@
|
||||
| `CHANNEL` | 自然语言生成交易渠道 |
|
||||
| `RULE` | 由已打标签记录生成分类与标签规则 |
|
||||
| `STATISTICS` | 自然语言统计查询解析 |
|
||||
| `RELATION` | 由关联账单生成关系名称与备注 |
|
||||
| `SQL` | 自然语言转换为只读 SQL 查询(智能查数) |
|
||||
|
||||
## 分类/标签建议
|
||||
|
||||
@@ -75,7 +78,7 @@
|
||||
|
||||
**实时进度**:`AiImageRecordStreamService` 基于 SSE 将上述步骤的进度文本推送前端——`POST /api/ai/record/image/stream` 返回 `SseEmitter`,识别在后台线程执行,过程中逐条推送 `progress` 事件,结束推送 `done`(完整结果 JSON)或 `failed`(失败原因)。
|
||||
|
||||
**图片访问**:`GET /api/ai/image/{id}` 返回原图,`?thumbnail=true` 返回最长边 1280 的 JPEG 缩略图;仅图片所属用户可访问。交易列表的「来源图片」默认加载缩略图,用户可按需切换原图。
|
||||
**图片访问**:`GET /api/ai/image/{id}` 返回原图,`?thumbnail=true` 返回最长边 1280 的 JPEG 缩略图;仅图片所属用户可访问。收支记录「账单详情」弹窗中的来源图片默认加载缩略图,用户可按需切换原图。
|
||||
|
||||
## 规则生成
|
||||
|
||||
@@ -89,6 +92,24 @@
|
||||
|
||||
`AiStatisticsServiceImpl` 在提示词中注入分类、标签与当前日期,将自然语言解析为统计面板参数:时间范围(预设→绝对时间)、收支类型、分类、标签、交易渠道、金额区间、备注、统计口径(`statsScope`)与趋势粒度(`dimension`)。
|
||||
|
||||
## 生成账单关系名称
|
||||
|
||||
`AiRelationServiceImpl` 依据用户新建关系组时勾选的**关联账单**生成名称与备注,对应 `POST /api/ai/relation`。
|
||||
|
||||
- 请求体为 `AiRelationRequest`(`type` + `members`);每个成员仅携带白名单字段——**收支类型、交易渠道、分类名称与备注**(`AiRelationMemberRequest` 明确不含金额、交易号与 `rawData`)。
|
||||
- 用户提示词由服务端集中拼接(关系类型中文名 + 成员字段,备注截断 30 字,最多 20 条);系统提示词模板 `ai-relation-system` 要求输出 4–12 字名称、≤40 字备注,并说明借贷场景(借出/归还、分次归还)的判定,只输出 JSON。
|
||||
- 解析后名称截断 ≤50 且必须非空、备注 ≤200;经 `AiUsageService` 记录 `AiCallType.RELATION` 用量。前端在[账单关系](/guide/relations)编辑弹窗中用它覆盖名称(备注非空时覆盖备注)。
|
||||
|
||||
## 智能查数(Text-to-SQL)
|
||||
|
||||
`AiSqlQueryService` 将自然语言交给大模型生成**只读 SQL** 并执行,配套独立的提问历史,对应 `POST /api/ai/sql`。
|
||||
|
||||
- 系统提示词模板 `ai-sql-system`:按当前数据源方言(SQLite / MySQL)注入日期函数说明与**动态表结构**——`TextToSqlSchemaProvider` 从数据库元数据读取列(字段增删改自动同步),并剔除 `raw_data`、`external_no` 等敏感列;要求模型只输出 `{sql, explanation, chartType}`,且**不要自带 `user_id`**(由网关统一注入)。
|
||||
- **安全网关** `cn.rosercode.ledgerly.util.sql.TextToSqlGuard` 是唯一执行通道:仅允许单条 `SELECT`(拒绝写操作/DDL/多语句/`SELECT INTO`/`FOR UPDATE`/危险函数);表白名单由 `TextToSqlTables` 静态登记(`t_transaction`、`t_category`、`t_tag`、`t_channel`、`t_transaction_group`、`t_rule` 及无 `user_id` 的关联表,新增可查表必须显式加入);对每张表强制注入 `user_id` 隔离条件(主表加 `WHERE`、JOIN 表加 `ON`、关联表用父表归属子查询);强制行数上限 `MAX_ROWS = 1000`。
|
||||
- 结果 VO `AiSqlQueryResultVO` 含 `sql`、`explanation`、`chartType`(`SCALAR`/`TABLE`/`BAR`/`PIE`)、`columns`、`rows`、`rowCount`、`truncated`;列类型映射为 `NUMBER`/`STRING`/`DATE`/`DATETIME`/`BOOLEAN`。
|
||||
- **提问历史**:`AiSqlHistoryService` 在每次成功后写入 `t_ai_sql_history`(按用户),提供 `GET /api/ai/sql/history`(limit 上限 200)与 `DELETE /api/ai/sql/history`。
|
||||
- 用量记录 `AiCallType.SQL`。
|
||||
|
||||
## 用量记录
|
||||
|
||||
- 每次调用写入 `t_ai_usage`:调用类型、模型、分析条数、输入/输出/总/缓存 token、是否成功、错误信息、耗时。
|
||||
|
||||
+22
-7
@@ -28,21 +28,22 @@
|
||||
|
||||
| 控制器 | 路径前缀 | 说明 |
|
||||
|---|---|---|
|
||||
| `UserController` | `/api/user` | 登录、注册、用户信息、注册开关 |
|
||||
| `TransactionController` | `/api/transaction` | 收支记录增删改查、批量创建与操作、导出、交易渠道选项 |
|
||||
| `UserController` | `/api/user` | 登录、注册、用户信息、注册开关、修改密码 |
|
||||
| `TransactionController` | `/api/transaction` | 收支记录增删改查、批量创建与操作、备注隐藏、导出、交易渠道选项 |
|
||||
| `TransactionGroupController` | `/api/transaction/group` | 账单关系组的查询、创建、更新、删除与名称历史 |
|
||||
| `BillImportController` | `/api/transaction/import` | 账单解析与确认导入 |
|
||||
| `ChannelController` | `/api/channel` | 用户自定义交易渠道的增删改查 |
|
||||
| `CategoryController` | `/api/category` | 分类管理 |
|
||||
| `TagController` | `/api/tag` | 标签管理 |
|
||||
| `RuleController` | `/api/rule` | 自定义规则(列表、分页查询、增删改、启停) |
|
||||
| `RuleController` | `/api/rule` | 自定义规则(列表、分页查询、增删改、启停、批量删除) |
|
||||
| `StatisticsController` | `/api/statistics` | 统计汇总、分类、趋势、分组 |
|
||||
| `AiController` | `/api/ai` | AI 建议、查询、文字/图片记账、渠道生成、规则生成、统计搜索、用量 |
|
||||
| `AiController` | `/api/ai` | AI 建议、查询、智能查数、文字/图片记账、渠道/关系/规则生成、统计搜索、用量 |
|
||||
| `NotificationController` | `/api/notification` | 通知配置、渠道、发送日志 |
|
||||
| `EmailBillController` | `/api/email-bill` | 邮箱配置、发件人规则、待处理账单 |
|
||||
| `LogController` | `/api/log` | 访问日志、登录日志、运行日志与 SSE |
|
||||
| `DatabaseController` | `/api/database` | 数据库状态、测试与配置 |
|
||||
| `DatabaseController` | `/api/database` | 数据库状态、测试与配置(含 SSE 进度) |
|
||||
| `AppConfigController` | `/api/app` | 应用配置(通知链接、日志保留) |
|
||||
| `UserSettingController` | `/api/setting` | 用户偏好(账单来源统计默认值、不计入统计展示样式) |
|
||||
| `UserSettingController` | `/api/setting` | 用户偏好(账单来源统计默认值、展示样式、隐藏备注显示、分页、导航) |
|
||||
|
||||
## 关键接口
|
||||
|
||||
@@ -50,16 +51,30 @@
|
||||
|---|---|---|
|
||||
| GET | `/api/transaction/channels` | 获取交易渠道选项(内置 + 当前用户自定义,含 `code`/`name`/`builtin`) |
|
||||
| POST | `/api/transaction/batch` | 批量创建交易记录(逐条可带 `imageId`/`rawData`),导入时按「类型+金额+时间」跳过重复 |
|
||||
| PUT | `/api/transaction/{id}/remark-hidden` | 隐藏 / 取消隐藏某条记录的备注 |
|
||||
| GET / POST / PUT / DELETE | `/api/channel`、`/api/channel/{id}` | 自定义渠道列表(含使用账单数)、新建、改名、删除 |
|
||||
| GET | `/api/transaction/group/by-transaction/{id}` | 查询某笔账单所属的全部关系组(含成员快照) |
|
||||
| GET | `/api/transaction/group/names` | 关系名称历史(可按 `type` 过滤,去重、倒序) |
|
||||
| POST / PUT / DELETE | `/api/transaction/group`、`/api/transaction/group/{id}` | 创建 / 更新 / 删除账单关系组(删除仅解除关联,不删除账单) |
|
||||
| PUT | `/api/user/password` | 修改当前登录用户的密码(校验原密码,新旧需不同) |
|
||||
| POST | `/api/ai/record/image` | 上传一至多张账单/支付截图(字段 `files`),调用视觉模型识别为多条交易记录 |
|
||||
| POST | `/api/ai/record/image/stream` | 图片记账(SSE):实时推送识别进度,结束返回完整结果 |
|
||||
| GET | `/api/ai/image/{id}` | 查看本人上传的图片记账图片,`?thumbnail=true` 返回缩略图 |
|
||||
| POST | `/api/ai/channel` | 由自然语言描述生成交易渠道名称 |
|
||||
| POST | `/api/ai/relation` | 由关联账单生成关系名称与备注 |
|
||||
| POST | `/api/ai/sql` | 智能查数:自然语言生成只读 SQL 并执行 |
|
||||
| GET / DELETE | `/api/ai/sql/history` | 查询 / 清空智能查数提问历史(`limit` 上限 200) |
|
||||
| GET | `/api/rule/page` | 分页查询规则(`keyword` / `enabled` / `type`) |
|
||||
| DELETE | `/api/rule/batch` | 批量删除规则(body `{ ids }`,返回实际删除数) |
|
||||
| POST | `/api/ai/rule/suggest` | 依据所选已打标签记录生成规则建议(含 `duplicate` 标记) |
|
||||
| POST | `/api/ai/rule/apply` | 批量创建规则(自动去重、按需新建分类/标签) |
|
||||
| POST | `/api/ai/statistics` | 自然语言解析为统计面板参数 |
|
||||
| GET / PUT | `/api/app/bill-source-include-in-stats` | 查询 / 更新按账单来源的「计入统计」默认值 |
|
||||
| GET / PUT | `/api/setting/excluded-display-style` | 查询 / 更新「不计入统计」记录展示样式 |
|
||||
| GET / PUT | `/api/setting/show-hidden-data` | 查询 / 更新是否显示已隐藏备注的记录 |
|
||||
| GET / PUT | `/api/setting/pagination-prefs` | 查询 / 更新表格分页候选列表与默认每页条数 |
|
||||
| GET / PUT | `/api/setting/nav-menu` | 查询 / 更新左侧导航顺序与显隐 |
|
||||
| GET / PUT | `/api/setting/bill-source-include-in-stats` | 查询 / 更新按账单来源的「计入统计」默认值 |
|
||||
| POST | `/api/database/config/stream` | 应用数据库配置并通过 SSE 推送进度(另有 `/api/database/test/stream`) |
|
||||
|
||||
## 统一响应结构
|
||||
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@
|
||||
| 认证 | JWT(HMAC) |
|
||||
| 对象映射 | MapStruct 1.6.3 |
|
||||
| 账单解析 | EasyExcel(xlsx)/ Apache POI(xls)+ commons-csv + PDFBox + Zip4j |
|
||||
| AI | DeepSeek(OpenAI Java SDK) |
|
||||
| AI | DeepSeek(OpenAI 兼容接口,Spring RestClient) |
|
||||
| 通知 | Jakarta Mail(SMTP / IMAP)+ Server 酱 |
|
||||
| API 文档 | Knife4j(OpenAPI 3) |
|
||||
|
||||
@@ -91,8 +91,8 @@ src/
|
||||
1. **运行时动态数据源**:启动无需数据库,配置后热切换,见[动态数据源](/dev/database)。
|
||||
2. **账单两阶段导入**:解析预览 → 确认导入,按交易号去重,见[账单解析](/dev/bill-parsing)。
|
||||
3. **规则引擎**:分类取最高优先级、标签取并集,见[规则引擎](/dev/rule-engine)。
|
||||
4. **体系化 AI 调用**:11 类调用类型并记录 token 用量,见 [AI 集成](/dev/ai)。
|
||||
5. **SSE 实时能力**:运行日志、邮箱检查进度与图片记账识别进度实时推送。
|
||||
4. **体系化 AI 调用**:13 类调用类型并记录 token 用量,见 [AI 集成](/dev/ai)。
|
||||
5. **SSE 实时能力**:运行日志、邮箱检查进度、图片记账识别进度与数据库配置执行进度实时推送。
|
||||
6. **用户偏好缓存**:`t_user_preference` 的读取走 Spring Cache 进程内缓存(`ConcurrentMapCacheManager`),写入时按用户失效,切换数据源时整体清空。
|
||||
|
||||
## 相关文档
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
## 样例与实现位置
|
||||
|
||||
- 编码:GBK(解析器兼容 UTF-8 / UTF-8 BOM)
|
||||
- 解析器:`src/main/java/cn/rosercode/finance/importer/AlipayBillParser.java`
|
||||
- 落库服务:`src/main/java/cn/rosercode/finance/service/impl/BillImportServiceImpl.java`
|
||||
- 解析器:`src/main/java/cn/rosercode/ledgerly/importer/AlipayBillParser.java`
|
||||
- 落库服务:`src/main/java/cn/rosercode/ledgerly/service/impl/BillImportServiceImpl.java`
|
||||
|
||||
## 文件结构
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
- **时间兼容**:微信账单兼容 Excel 日期序列号与文本时间;支付宝在付款时间缺失时回退交易创建时间;淘宝订单同样兼容文本时间与 Excel 日期序列号。
|
||||
- **订单聚合**:淘宝订单数据中多商品订单的订单级列(订单号、提交时间、状态、店铺、实付金额、运费)以合并单元格呈现,仅首个商品行有值;解析器按订单号分组聚合,每个订单输出一条记录,金额优先取订单级实付金额,为空时回退「Σ(商品金额×数量)+运费」。
|
||||
- **不计收支处理**:京东流水的「不计收支」行(白条还款、小金库转入、全额退款等)标记为可导入但**不计入统计且默认不勾选**,类型按「交易说明含『退款』→ INCOME,否则 EXPENSE」推断。
|
||||
- **计入统计默认值**:解析完成后按**账单来源**应用「计入统计」默认值(配置于系统设置,见 [动态数据源](/dev/database) 同级的应用配置)。采用「解析器判定 && 来源默认」语义——来源默认不计入时整批置为不计入;来源默认计入时保留解析器对「不计收支/中性」行的排除。确认导入时若请求未显式提供该字段,同样以来源默认兜底。
|
||||
- **计入统计默认值**:解析完成后按**账单来源**应用「计入统计」默认值(配置于设置页,见 [动态数据源](/dev/database) 同级的应用配置)。采用「解析器判定 && 来源默认」语义——来源默认不计入时整批置为不计入;来源默认计入时保留解析器对「不计收支/中性」行的排除。确认导入时若请求未显式提供该字段,同样以来源默认兜底。
|
||||
|
||||
## 压缩包解密
|
||||
|
||||
|
||||
+96
-40
@@ -1,9 +1,12 @@
|
||||
# 构建与部署
|
||||
|
||||
本章介绍「汇账」的构建方式、分发形态与生产部署流程。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- **JDK 21**(Temurin 21 LTS)
|
||||
- **JDK 21**(Temurin 21 LTS,含 `jpackage`)
|
||||
- **Node.js**(构建前端时需要)
|
||||
- 依赖仓库:本地 Nexus(Maven)与 verdaccio(npm);构建时经 `-I nexus-init.gradle` 注入 Maven 私服地址
|
||||
|
||||
## 开发模式
|
||||
|
||||
@@ -12,85 +15,138 @@
|
||||
```bash
|
||||
# 后端(默认 8080)
|
||||
cd ledgerly
|
||||
set JAVA_HOME=D:\venv\jdk-21
|
||||
gradlew.bat bootRun
|
||||
./gradlew bootRun # Windows: gradlew.bat bootRun
|
||||
|
||||
# 前端(默认 5173,接口代理到 8080)
|
||||
cd ledgerly/frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- 后端地址:http://localhost:8080
|
||||
- 后端 API 文档:http://localhost:8080/doc.html
|
||||
- 前端地址:http://localhost:5173,接口经 Vite 代理到后端 8080
|
||||
- 后端地址:`http://localhost:8080`,接口文档 `http://localhost:8080/doc.html`
|
||||
- 前端地址:`http://localhost:5173`,`/api` 经 Vite 代理到后端 8080
|
||||
|
||||
## 打包可执行 jar(前后端一体化)
|
||||
## 构建可执行 jar(前后端一体化)
|
||||
|
||||
`bootJar` 会先执行前端 `npm run build`,再把 `frontend/dist` 复制到 jar 内的 `BOOT-INF/classes/static/`,单文件同时提供页面与接口。
|
||||
|
||||
```bash
|
||||
gradle bootJar
|
||||
# 或使用宿主机脚本
|
||||
# 普通 jar(无 GUI,不含 JavaFX)
|
||||
./gradlew -I nexus-init.gradle bootJar
|
||||
|
||||
# 带 GUI 启动器的 jar
|
||||
./gradlew -I nexus-init.gradle bootGuiJar
|
||||
|
||||
# 或使用宿主机脚本(一次产出两者)
|
||||
./package.sh
|
||||
```
|
||||
|
||||
输出:`build/libs/ledgerly-0.0.1-SNAPSHOT.jar`
|
||||
产物文件名携带目标 JDK 版本:
|
||||
|
||||
| 任务 | 产物 | 说明 |
|
||||
|---|---|---|
|
||||
| `bootJar` | `build/libs/ledgerly-0.1-jdk21.jar` | 普通服务 jar |
|
||||
| `bootGuiJar` | `build/libs/ledgerly-0.1-jdk21-gui.jar` | 入口为桌面启动器 |
|
||||
|
||||
启动(默认 8080,可另指定端口):
|
||||
|
||||
```bash
|
||||
java -jar build/libs/ledgerly-0.0.1-SNAPSHOT.jar --server.port=18081
|
||||
java -jar build/libs/ledgerly-0.1-jdk21.jar --server.port=18081
|
||||
```
|
||||
|
||||
访问对应端口即可;刷新 `/dashboard` 等子路由由后端 `SpaErrorController` 回退到 `index.html`。
|
||||
构建任务链(`build.gradle`):`npmInstall` → `frontendBuild` → `bootJar`。
|
||||
|
||||
::: tip 提示
|
||||
jar 内置的前端为生产构建,访问时与后端同源,无需额外的反向代理。
|
||||
:::
|
||||
|
||||
构建任务链(`build.gradle`):
|
||||
为减小可执行 jar 体积,普通 jar 与 GUI jar 均做了裁剪:AI 调用改用 Spring `RestClient` 直连 OpenAI 兼容接口(不再打包 OpenAI Java SDK);接口文档仅保留 `/v3/api-docs`(JSON),Knife4j 的 `doc.html` UI 依赖为 `developmentOnly`,不进生产 jar;普通 jar 通过 `exclude` 排除桌面启动器包,保持无 JavaFX 依赖。
|
||||
|
||||
- `npmInstall`:安装前端依赖(`node_modules` 存在且 `package.json` 未变化时跳过)
|
||||
- `frontendBuild`:执行 `npm run build`
|
||||
- `bootJar`:依赖 `frontendBuild`,并将前端产物打入静态资源目录
|
||||
运行时首次配置或切换数据库时,「测试连接」与「保存并应用」会通过 SSE **实时推送执行进度**(连接测试、建表、结构迁移、数据源切换等)。
|
||||
|
||||
## Docker 部署
|
||||
## 构建免安装应用(jpackage app-image)
|
||||
|
||||
`run/docker-compose.yml` 定义前后端两个服务,纯复用本地镜像、不构建新镜像:
|
||||
`jpackageAppImage` 聚合任务一次生成两个 app-image,自带精简运行时,双击即用;**仅能为当前操作系统生成**(Windows 上生成 Windows 产物,Linux 上生成 Linux 产物)。
|
||||
|
||||
| 服务 | 镜像 | 端口 | 说明 |
|
||||
```bash
|
||||
./gradlew -I nexus-init.gradle jpackageAppImage
|
||||
# 也可单独执行 jpackageServerAppImage / jpackageGuiAppImage
|
||||
```
|
||||
|
||||
| 任务 | 应用名 | 源 jar | 入口 |
|
||||
|---|---|---|---|
|
||||
| backend | `eclipse-temurin:21-jre` | 18080→8080 | 挂载 `run/backend/*.jar`,持久化 `config/`、`data/`、`logs/` |
|
||||
| front | `nginx:alpine` | 15180→80 | 挂载 `run/front/dist` 与 Nginx 配置 |
|
||||
| `jpackageServerAppImage` | `LedgerlyServer` | `bootJar` | `bin/LedgerlyServer`(Windows 为 `LedgerlyServer.exe`,附带控制台) |
|
||||
| `jpackageGuiAppImage` | `Ledgerly` | `bootGuiJar` | `bin/Ledgerly`(Windows 为 `Ledgerly.exe`) |
|
||||
|
||||
环境变量 `DB_URL` / `DB_USERNAME` / `DB_PASSWORD` 仅作为首次启动的默认配置来源,也可在界面中完成数据库配置。
|
||||
产物位于 `build/jpackage/LedgerlyServer/` 与 `build/jpackage/Ledgerly/`。
|
||||
|
||||
图标不随源码分发:打包时 `downloadAppIcon` 从 `src/main/resources/launcher/icon.url` 下载源图,再由 `tools/IconGen.java` 生成平台图标(`ico` / `icns` / `png`);可用 `-PsourceIcon=<png>` 指定本地源图。
|
||||
|
||||
## 桌面 GUI 启动器
|
||||
|
||||
`launcher` 包提供系统托盘 + 控制窗口的桌面启动器(启动/停止服务、打开浏览器、查看日志),通过 `bootGuiJar` 打包:
|
||||
|
||||
```bash
|
||||
gradle bootGuiJar
|
||||
```
|
||||
|
||||
- 入口类:`cn.rosercode.finance.launcher.LauncherMain`
|
||||
- 依赖 JavaFX 与 dorkbox SystemTray,仅加入带 GUI 的 jar(`guiRuntime` 配置),普通 jar 不含这些依赖
|
||||
- 平台默认 Windows(可通过 `-PjavafxPlatform=linux` 本地验证)
|
||||
- JavaFX 与 JNA 原生库需 `requiresUnpack` 从嵌套 jar 解压
|
||||
|
||||
> 注意:该启动器为规划中的能力,具体实现以当前源码为准。
|
||||
`cn.rosercode.ledgerly.launcher` 提供系统托盘 + 控制窗口的桌面启动器(进程内启停服务、设置端口、打开浏览器、实时查看日志、单实例运行),仅打入带 GUI 的 jar,普通 jar 不含 JavaFX 依赖。
|
||||
|
||||
## 分发形态
|
||||
|
||||
| 形态 | 说明 |
|
||||
|---|---|
|
||||
| jar | `bootJar`,需 JDK 21 运行 |
|
||||
| jar(带 GUI) | `bootGuiJar`,内含桌面启动器 |
|
||||
| 可执行文件(控制台 / GUI) | 规划中,计划采用 `jpackage`(JDK 自带,各平台分别构建) |
|
||||
| Docker 镜像 | 通过 `run/docker-compose.yml` 编排 |
|
||||
| 形态 | 构建方式 | 说明 |
|
||||
|---|---|---|
|
||||
| 普通 jar | `bootJar` | 需 JDK 21 运行,跨平台 |
|
||||
| 带 GUI 启动器 jar | `bootGuiJar` | 内含桌面启动器,跨平台(打包含 JavaFX 全平台原生库) |
|
||||
| Windows 免安装包 | `jpackageAppImage` | 在 Windows 上构建,zip 分发 |
|
||||
| Linux 免安装包 | `jpackageAppImage` | 在 Linux 上构建,tar.gz 分发 |
|
||||
|
||||
## 发布产物与下载地址
|
||||
|
||||
发布产物上传至阿里云 OSS(桶 `adccrac`,对象键前缀 `ledgerly/release/<版本>/`),通过 10 年有效的长期签名 URL 对外提供下载;每个文件同时提供 `.sha256` 校验文件与汇总清单 `SHA256SUMS`。
|
||||
|
||||
当前工程版本为 `0.1`;最新下载地址见使用文档的[下载与安装](/guide/download)(该页链接指向已上传的发布产物)。
|
||||
|
||||
上传方式:
|
||||
|
||||
- Linux 产物(jar 与 Linux app-image 包):工作区根 `oss-release-upload.py`,需在带 `oss2` 的虚拟环境执行(如 `20260807_131528/oss-venv`),上传后输出长期签名 URL。
|
||||
- Windows 产物(Windows app-image 包):在 Windows 主机上用 Node 脚本(`ali-oss` + STS)直接上传,不回传本地。
|
||||
|
||||
## 生产部署(云服务器)
|
||||
|
||||
服务部署在云服务器 `101.35.20.223` 的 `/opt/ledgerly` 目录,由 Docker Compose 编排:
|
||||
|
||||
| 容器 | 镜像 | 端口 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `ledgerly` | `eclipse-temurin:21-jre` | `127.0.0.1:15180 → 8080` | 挂载 `app/ledgerly-0.1.jar`、`config/`、`data/`,JVM `-Xmx512m` |
|
||||
| `ledgerly-docs` | `nginx:1.27` | `127.0.0.1:15181 → 80` | 挂载 `docs-dist/` 与 `docs/nginx.conf` |
|
||||
|
||||
云上 `nginx` 容器反代这两个回环端口并终止 HTTPS,证书由 `certbot` 容器负责续期:
|
||||
|
||||
- 服务:[https://finance.rosercode.cn](https://finance.rosercode.cn)
|
||||
- 文档:[https://docs.finance.rosercode.cn](https://docs.finance.rosercode.cn)
|
||||
|
||||
### 发布流程
|
||||
|
||||
服务(构建 → 复制 → 重启):
|
||||
|
||||
```bash
|
||||
# 1) 本地构建 jar(见上)
|
||||
# 2) 复制到云端部署目录,目标文件名须与 compose 挂载一致
|
||||
scp -i ~/.ssh/id_ed25519_www.rosercode.cn \
|
||||
build/libs/ledgerly-0.1-jdk21.jar \
|
||||
root@101.35.20.223:/opt/ledgerly/app/ledgerly-0.1.jar
|
||||
# 3) 重启容器
|
||||
ssh -i ~/.ssh/id_ed25519_www.rosercode.cn root@101.35.20.223 \
|
||||
'cd /opt/ledgerly && docker compose restart'
|
||||
```
|
||||
|
||||
文档站(构建 → 覆盖 `docs-dist`,目录挂载即时生效):
|
||||
|
||||
```bash
|
||||
cd doc-site && npm run docs:build
|
||||
scp -i ~/.ssh/id_ed25519_www.rosercode.cn -r .vitepress/dist/* \
|
||||
root@101.35.20.223:/opt/ledgerly/docs-dist/
|
||||
```
|
||||
|
||||
> 云服务器不能直连 DockerHub,新镜像需本地 `docker save | gzip` → `scp` → 云端 `docker load` 导入。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [项目结构](/dev/project-structure)
|
||||
- [开发环境与规范](/dev/development)
|
||||
- [下载与安装(使用文档)](/guide/download)
|
||||
- [快速开始(使用文档)](/guide/getting-started)
|
||||
|
||||
+15
-2
@@ -1,6 +1,6 @@
|
||||
# 数据模型
|
||||
|
||||
系统共 **21 张表**,覆盖用户、记账、渠道、标签、规则、AI、通知、邮箱账单与日志审计。所有表主键为自增 `id`,表间关系均为**逻辑外键**(通过 `user_id`、`category_id` 等字段在应用层维护,未声明物理外键)。
|
||||
系统共 **24 张表**,覆盖用户、记账、账单关系、渠道、标签、规则、AI、通知、邮箱账单与日志审计。所有表主键为自增 `id`,表间关系均为**逻辑外键**(通过 `user_id`、`category_id` 等字段在应用层维护,未声明物理外键)。
|
||||
|
||||
## 表清单
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
| 记账 | `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` | 通知发送日志 |
|
||||
@@ -38,6 +41,7 @@
|
||||
| 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 |
|
||||
@@ -64,6 +68,8 @@
|
||||
| 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`(用户内唯一),因此渠道改名不影响历史记录。
|
||||
|
||||
@@ -106,6 +112,12 @@
|
||||
|
||||
`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`;一笔账单不能同时加入两个**同类型**的关系组。删除账单时会自动清理其成员记录,成员不足两条或父子组缺父时整组删除(仅解除关联,不删除账单)。详见[账单关系(使用说明)](/guide/relations)。
|
||||
|
||||
### t_channel(自定义交易渠道)
|
||||
|
||||
| 字段 | 说明 |
|
||||
@@ -122,12 +134,13 @@
|
||||
- `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` 为偏好键(如 `billSourceIncludeInStats`、`excludedDisplayStyle`),`pref_value` 为标量字符串或 JSON。唯一索引 `uk_user_pref (user_id, pref_key)` 保证每个用户每个偏好仅一条。未配置时由应用回退系统默认值。
|
||||
按用户存储的键值偏好:`pref_key` 为偏好键,`pref_value` 为标量字符串或 JSON。已使用的偏好键包括:`billSourceIncludeInStats`(各来源计入统计默认值)、`excludedDisplayStyle`(不计入记录展示样式)、`showHiddenData`(是否显示已隐藏备注的记录)、`paginationPrefs`(表格分页候选与默认值)、`navMenu`(左侧导航顺序与显隐)。唯一索引 `uk_user_pref (user_id, pref_key)` 保证每个用户每个偏好仅一条。未配置时由应用回退系统默认值。
|
||||
|
||||
### t_email_bill_config
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ MySQL 的 JDBC URL 会自动附带 UTF-8、关闭 SSL、`Asia/Shanghai` 时区
|
||||
|
||||
## 切换行为
|
||||
|
||||
登录后可在「系统设置 → 数据库配置」随时切换数据库。切换后原数据库的数据保留,**不做自动迁移**。
|
||||
登录后可在「设置 → 数据库配置」随时切换数据库;测试与保存时通过 SSE 实时推送执行进度。切换后原数据库的数据保留,**不做自动迁移**。
|
||||
|
||||
切换成功后会清空进程内的用户偏好缓存(见[整体架构](/dev/architecture)),避免读到旧库的数据。
|
||||
|
||||
|
||||
+6
-3
@@ -20,6 +20,8 @@
|
||||
- 前端改动**不使用 `npm run build` 验证**,直接依赖 Vite 开发服务器的 HMR。
|
||||
- `npm run build`(`vue-tsc -b && vite build`)的产物仅用于生产部署,与开发模式无关。
|
||||
- 界面文案统一在 `frontend/src/locales/messages.ts` 维护,新增 key 需同时提供中文(zh)、英文(en)、日文(ja)三语。
|
||||
- 界面偏好需持久化记忆,存储键统一以 `ledgerly.` 前缀命名并集中维护于 `frontend/src/utils/storageKeys.ts`,读写经 `frontend/src/utils/storage.ts`(禁止业务代码直接调用 `localStorage`/`sessionStorage`);旧 `finance.` 前缀由 `migrateLegacyPrefixedKeys()` 自动迁移。详见[前端用户偏好](/dev/preferences)。
|
||||
- **与 AI 相关的界面元素必须受 AI 启用状态控制**:未启用时一律不渲染(`v-if="aiEnabled"`),启用状态统一走共享状态 `frontend/src/composables/useAiEnabled.ts`(侧边栏「智能查数」菜单项亦据此显隐)。
|
||||
|
||||
## 后端开发
|
||||
|
||||
@@ -41,7 +43,7 @@
|
||||
- 格式化:`docker exec ledgerly-backend-dev gradle -I /project/nexus-init.gradle spotlessApply`
|
||||
- `spotlessCheck` 已挂到 `check`,`build` 时会校验。
|
||||
- **日志**:统一使用 Lombok `@Slf4j`,按级别使用(`error` 未预期异常、`warn` 可预期业务失败、`info` 关键写操作与外部调用、`debug` 流程细节)。
|
||||
- **控制器**:方法签名禁止声明 `HttpServletRequest` / `HttpServletResponse`,统一使用 `cn.rosercode.finance.util.UserContext`。
|
||||
- **控制器**:方法签名禁止声明 `HttpServletRequest` / `HttpServletResponse`,统一使用 `cn.rosercode.ledgerly.util.UserContext`。
|
||||
- **数据库表结构变更**:需同时维护 `sql/init.sql`(MySQL)与 `sql/schema-sqlite.sql`(SQLite),SQLite 以 `;;` 分隔语句。
|
||||
- **对象转换**:实体与 VO 之间统一使用 MapStruct(`converter.VoConverter`),不在 Service 中手写 setter。
|
||||
- **定时任务**:基于 Spring `@Scheduled`,新增调度器直接标注即可。
|
||||
@@ -49,8 +51,9 @@
|
||||
## Git 提交规范
|
||||
|
||||
- 提交范围仅包含源码与配置;个人账单原始数据、`docs/`、`reload.sh`、`.gitignore`、`nexus-init.gradle` 及 AI 测试环境基础设施不纳入提交。
|
||||
- commit message 使用 `feat: 中文描述` 风格;纯重构/规范调整用 `refactor:`,构建与依赖配置用 `chore:` 或 `build:`。
|
||||
- 远端为本地 Gitea,主分支 `master`;推送使用一次性内嵌令牌的 URL,避免令牌写入 `origin` 配置。
|
||||
- commit message 采用「标题 + 概述段 + 分组列表」结构;标题为 `<type>: <中文简述>`,纯重构/规范调整用 `refactor:`,构建与依赖配置用 `chore:` 或 `build:`。
|
||||
- 远端为本地 Gitea,**提交与推送仅使用 `dev` 分支**(`master` 仅用于稳定/发布,不纳入日常开发);推送使用一次性内嵌令牌的 URL,避免令牌写入 `origin` 配置。
|
||||
- 每次创建 commit 后需执行 `./package.sh` 打包验证,并在 `frontend/` 执行一次 `npx vue-tsc -b --force` 类型检查。
|
||||
|
||||
## 相关文档
|
||||
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@
|
||||
## 设计与实现
|
||||
|
||||
- [整体架构](/dev/architecture):技术栈、后端分层、请求流程、认证与前端结构
|
||||
- [数据模型](/dev/data-model):21 张表的字段与关系、双数据库脚本差异
|
||||
- [数据模型](/dev/data-model):24 张表的字段与关系、双数据库脚本差异
|
||||
- [账单解析](/dev/bill-parsing):解析器策略模式、两阶段导入、去重与 zip 解密
|
||||
- [规则引擎](/dev/rule-engine):规则模型与匹配逻辑
|
||||
- [AI 集成](/dev/ai):OpenAI SDK 客户端、11 类调用类型与用量记录
|
||||
- [AI 集成](/dev/ai):RestClient 客户端、13 类调用类型与用量记录
|
||||
- [定时通知](/dev/notifications):调度、渠道与发送日志
|
||||
- [动态数据源](/dev/database):SQLite / MySQL 运行时配置与切换
|
||||
- [前端用户偏好](/dev/preferences):偏好的存储介质、读写时机与完整清单
|
||||
@@ -16,7 +16,7 @@
|
||||
## 工程与规范
|
||||
|
||||
- [项目结构](/dev/project-structure):目录组织与各包职责
|
||||
- [构建与部署](/dev/build-deploy):`bootJar`、Docker 编排、桌面 GUI 启动器与 jpackage
|
||||
- [构建与部署](/dev/build-deploy):`bootJar` / `bootGuiJar`、jpackage 免安装包、发布产物下载与云服务器部署
|
||||
- [开发环境与规范](/dev/development):AI 测试环境、前后端开发流程、代码规范、Git 提交规范
|
||||
- [账单字段说明](/dev/bill-fields):支付宝账单导入字段的读取与丢弃情况
|
||||
- [API 文档](/dev/api):Knife4j(OpenAPI 3)接口文档
|
||||
|
||||
+84
-39
@@ -8,7 +8,7 @@
|
||||
|
||||
为此项目在 `ledgerly/AGENTS.md` 中约定:
|
||||
|
||||
> 多 Tab 或可切换的界面偏好需**持久化记忆**(`localStorage`),下次打开时恢复到上次选择;存储键统一以 `finance.` 前缀命名,读取时对非法值回退默认项,写入时忽略存储异常。
|
||||
> 多 Tab 或可切换的界面偏好需**持久化记忆**(`localStorage`),下次打开时恢复到上次选择;存储键统一以 `ledgerly.` 前缀命名,读取时对非法值回退默认项,写入时忽略存储异常。
|
||||
|
||||
据此形成三条实践原则:
|
||||
|
||||
@@ -32,38 +32,45 @@
|
||||
|
||||
| 键名 | 偏好内容 | 文件 | 保存时机 | 读取时机 |
|
||||
|---|---|---|---|---|
|
||||
| `locale` | 界面语言 `zh-CN`/`en-US`/`ja-JP` | `locales/index.ts:16` | 切换语言 | 模块初始化 |
|
||||
| `finance.dashboard.prefs` | 统计面板时间范围、粒度与 11 项筛选 | `views/Dashboard.vue:470` | 变量 `watch` 自动 | `onMounted` |
|
||||
| `finance.transaction.filterPrefs` | 收支列表筛选、分页、排序 | `views/TransactionList.vue:369` | 变量 `watch` 自动 | `onMounted` |
|
||||
| `finance.transaction.aiScope` | 批量 AI 建议范围 | `views/TransactionList.vue:146` | 变量 `watch` 自动 | ref 初始化 |
|
||||
| `finance.transaction.aiRecordTab` | AI 记账方式(文字/图片) | `views/TransactionList.vue:206` | 变量 `watch` 自动 | ref 初始化 |
|
||||
| `finance.transaction.aiQueryHistory` | AI 自然语言查询历史 | `views/TransactionList.vue:195` | 提交查询时 | `onMounted` |
|
||||
| `finance.transaction.remarkHistory` | 备注关键词历史 | `views/TransactionList.vue:300` | 备注失焦/回车 | `onMounted` |
|
||||
| `finance.dashboard.aiSearchHistory` | 统计面板 AI 查询历史 | `views/Dashboard.vue:91` | 提交 AI 搜索时 | `onMounted` |
|
||||
| `finance.billImport.billType` | 上次选择的账单来源类型 | `views/BillImportPanel.vue:25` | 切换类型时 | ref 初始化 |
|
||||
| `finance.transaction.relationType` | 账单关系类型偏好 | `components/TransactionRelationEditorModal.vue:14` | 创建模式切换类型时 | 弹窗初始化 |
|
||||
| `emailBill.tab` | 邮箱账单页标签页 | `views/EmailBillManage.vue:43` | Tab 切换时 | 路由重定向 + 组件 |
|
||||
| `logs.tab` | 系统日志页标签页 | `views/Logs.vue:25` | Tab 切换时 | 路由重定向 + 组件 |
|
||||
| `ledgerly.locale` | 界面语言 `zh-CN`/`en-US`/`ja-JP` | `locales/index.ts` | 切换语言 | 模块初始化 |
|
||||
| `ledgerly.dashboard.prefs` | 统计面板时间范围、粒度与多项筛选 | `views/Dashboard.vue` | 变量 `watch` 自动 | `onMounted` |
|
||||
| `ledgerly.transaction.filterPrefs` | 收支列表筛选、分页、排序 | `views/TransactionList.vue` | 变量 `watch` 自动 | `onMounted` |
|
||||
| `ledgerly.transaction.aiScope` | 批量 AI 建议范围 | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 |
|
||||
| `ledgerly.transaction.aiRecordTab` | AI 记账方式(文字/图片) | `views/TransactionList.vue` | 变量 `watch` 自动 | ref 初始化 |
|
||||
| `ledgerly.transaction.aiQueryHistory` | AI 自然语言查询历史 | `views/TransactionList.vue` / `components/AiQueryWindow.vue` | 提交查询时 | 浮窗聚焦时 |
|
||||
| `ledgerly.dashboard.aiSearchHistory` | 统计面板 AI 查询历史 | `views/Dashboard.vue` | 提交 AI 搜索时 | 浮窗聚焦时 |
|
||||
| `ledgerly.transaction.aiQueryWindow` | 收支记录 AI 查询浮窗位置与尺寸 | `components/FloatingPanel.vue` | 拖拽/缩放结束时 | 浮窗初始化 |
|
||||
| `ledgerly.dashboard.aiQueryWindow` | 统计面板 AI 搜索浮窗位置与尺寸 | `components/FloatingPanel.vue` | 拖拽/缩放结束时 | 浮窗初始化 |
|
||||
| `ledgerly.transaction.remarkHistory` | 备注关键词历史 | `views/TransactionList.vue` | 备注失焦/回车 | `onMounted` |
|
||||
| `ledgerly.billImport.billType` | 上次选择的账单来源类型 | `views/BillImportPanel.vue` | 切换类型时 | ref 初始化 |
|
||||
| `ledgerly.transaction.relationType` | 账单关系类型偏好 | `components/TransactionRelationEditorModal.vue` | 创建模式切换类型时 | 弹窗初始化 |
|
||||
| `ledgerly.pagination.tablePageSizes` | 各表格分别记住的每页条数 | `utils/tablePageSizePrefs.ts` | 切换每页条数时 | 表格初始化 |
|
||||
| `ledgerly.emailBill.tab` | 邮箱账单页标签页 | `views/EmailBillManage.vue` | Tab 切换时 | 路由重定向 + 组件 |
|
||||
| `ledgerly.logs.tab` | 日志页标签页 | `views/Logs.vue` | Tab 切换时 | 路由重定向 + 组件 |
|
||||
|
||||
> `locale`、`emailBill.tab`、`logs.tab` 是较早的实现,未使用 `finance.` 前缀;前两者分别为国际化与路由重定向的既有约定,改动时需保持键名稳定。
|
||||
> `locale`、`emailBill.tab`、`logs.tab` 是较早的实现,最初未加前缀,现已统一为 `ledgerly.` 前缀:在 `LEGACY_STORAGE_KEYS` 登记旧键、读取前用 `migrateKey` 一次性迁移;品牌改名(`finance.` → `ledgerly.`)的旧键由 `migrateLegacyPrefixedKeys()` 在模块加载时批量迁移,新键已存在时不覆盖。
|
||||
|
||||
### sessionStorage 偏好
|
||||
|
||||
| 键名 | 偏好内容 | 文件 |
|
||||
|---|---|---|
|
||||
| `finance.scroll.<路由路径>` | 各页面内容区滚动位置 | `views/Layout.vue:83` |
|
||||
| `ledgerly.scroll.<路由路径>` | 各页面内容区滚动位置 | `views/Layout.vue` |
|
||||
| `ledgerly.sqlQuery.session` | 智能查数当前会话的提问、结果与视图(结果超 500 行时只存提问与视图) | `views/SqlQuery.vue` |
|
||||
|
||||
### 后端按用户同步的偏好
|
||||
|
||||
| 接口 | 偏好内容 | 前端入口 |
|
||||
|---|---|---|
|
||||
| `GET/PUT /api/setting/excluded-display-style` | 「不计入统计」记录展示样式 | `components/DisplayConfigPanel.vue` |
|
||||
| `GET/PUT /api/setting/show-hidden-data` | 是否显示已隐藏备注的记录 | `components/DisplayConfigPanel.vue` |
|
||||
| `GET/PUT /api/setting/pagination-prefs` | 表格分页候选与默认每页条数 | `components/DisplayConfigPanel.vue` |
|
||||
| `GET/PUT /api/setting/nav-menu` | 左侧导航顺序与显隐 | `components/NavMenuConfigPanel.vue` |
|
||||
| `GET/PUT /api/setting/bill-source-include-in-stats` | 各账单来源「计入统计」默认值 | `components/BillSourceStatsPanel.vue` |
|
||||
| `GET/PUT /api/ai/config` | AI 启用开关、密钥、模型 | `components/AiConfigPanel.vue` |
|
||||
|
||||
## localStorage 偏好详解
|
||||
|
||||
### 界面语言 `locale`
|
||||
### 界面语言 `ledgerly.locale`
|
||||
|
||||
- **文件**:`frontend/src/locales/index.ts`
|
||||
- **取值**:`zh-CN`、`en-US`、`ja-JP`
|
||||
@@ -72,7 +79,7 @@
|
||||
- **目的**:记住用户语言选择,应用启动与切换即时生效。
|
||||
- **入口**:`components/LocaleSwitch.vue`。
|
||||
|
||||
### 统计面板偏好 `finance.dashboard.prefs`
|
||||
### 统计面板偏好 `ledgerly.dashboard.prefs`
|
||||
|
||||
- **文件**:`frontend/src/views/Dashboard.vue`(键名第 470 行)
|
||||
- **数据结构**(JSON 对象,保存见 `savePrefs()` 第 499 行):
|
||||
@@ -95,7 +102,7 @@
|
||||
- **跨页写入**:点击趋势图跳转收支列表时,`applyTrendNavigation()`(第 381 行)把统计口径与筛选**合并写入**收支列表的偏好键,并保留分页、排序等既有设置。
|
||||
- **目的**:下次进入统计面板自动还原时间范围、粒度与全部过滤条件。
|
||||
|
||||
### 收支列表筛选偏好 `finance.transaction.filterPrefs`
|
||||
### 收支列表筛选偏好 `ledgerly.transaction.filterPrefs`
|
||||
|
||||
- **文件**:`frontend/src/views/TransactionList.vue`(键名第 369 行)
|
||||
- **数据结构**(保存见 `saveFilterPrefs()` 第 396 行):
|
||||
@@ -121,17 +128,17 @@
|
||||
|
||||
| 键名 | 取值 | 说明 |
|
||||
|---|---|---|
|
||||
| `finance.transaction.aiScope` | `BOTH`/`CATEGORY`/`TAG` | 批量 AI 建议范围,非法值回退 `BOTH`(第 146 行) |
|
||||
| `finance.transaction.aiRecordTab` | `text`/`image` | AI 记账方式,非法值回退 `text`(第 206 行) |
|
||||
| `finance.transaction.aiQueryHistory` | 字符串数组(≤10) | AI 自然语言查询历史,最新在前 |
|
||||
| `finance.transaction.remarkHistory` | 字符串数组(≤10) | 备注关键词历史,失焦/回车记录 |
|
||||
| `ledgerly.transaction.aiScope` | `BOTH`/`CATEGORY`/`TAG` | 批量 AI 建议范围,非法值回退 `BOTH`(第 146 行) |
|
||||
| `ledgerly.transaction.aiRecordTab` | `text`/`image` | AI 记账方式,非法值回退 `text`(第 206 行) |
|
||||
| `ledgerly.transaction.aiQueryHistory` | 字符串数组(≤10) | AI 自然语言查询历史,最新在前 |
|
||||
| `ledgerly.transaction.remarkHistory` | 字符串数组(≤10) | 备注关键词历史,失焦/回车记录 |
|
||||
|
||||
- **`aiRecordTab`** 是 `AGENTS.md` 点名的参考实现:记住用户上次用的记账方式(文字或图片),下次打开 AI 记账弹窗直接落到该 Tab。
|
||||
- **关键词历史**统一由 `composables/useKeywordHistory.ts` 管理:去重(忽略大小写)、最新在前、上限 10 条,损坏数据自动忽略,下拉末尾提供「清空历史」项。
|
||||
- 读取:`load()`;记录:`record()`;清空:`clear()`。
|
||||
- 三处使用:AI 查询历史、备注历史(均在 `TransactionList.vue`)、统计面板 AI 搜索历史(`Dashboard.vue`,键 `finance.dashboard.aiSearchHistory`)。
|
||||
- 三处使用:AI 查询历史、备注历史(均在 `TransactionList.vue`)、统计面板 AI 搜索历史(`Dashboard.vue`,键 `ledgerly.dashboard.aiSearchHistory`)。
|
||||
|
||||
### 账单导入类型 `finance.billImport.billType`
|
||||
### 账单导入类型 `ledgerly.billImport.billType`
|
||||
|
||||
- **文件**:`frontend/src/views/BillImportPanel.vue`(第 25 行)
|
||||
- **取值**:账单来源编码(`ALIPAY`、`WECHAT`、`CMB` 等)
|
||||
@@ -139,7 +146,7 @@
|
||||
- **保存**:`watch(billType)`(第 226 行)在切换类型时写入,同时清空已选文件,避免格式与类型不匹配。
|
||||
- **目的**:记住上次选择的账单来源。
|
||||
|
||||
### 账单关系类型 `finance.transaction.relationType`
|
||||
### 账单关系类型 `ledgerly.transaction.relationType`
|
||||
|
||||
- **文件**:`frontend/src/components/TransactionRelationEditorModal.vue`(第 14 行)
|
||||
- **取值**:`PARENT_CHILD`(默认)、`SIBLING`
|
||||
@@ -147,18 +154,32 @@
|
||||
- **保存**:`watch(editorType)`(第 69 行)**仅在创建模式**(`editingGroupId === null`)写入,编辑既有关系组不覆盖偏好。
|
||||
- **目的**:新建关系组时默认沿用上次的关系类型。
|
||||
|
||||
### 页面标签页 `emailBill.tab` / `logs.tab`
|
||||
### 页面标签页 `ledgerly.emailBill.tab` / `ledgerly.logs.tab`
|
||||
|
||||
- **文件**:`views/EmailBillManage.vue:43`、`views/Logs.vue:25`
|
||||
- **文件**:`views/EmailBillManage.vue`、`views/Logs.vue`
|
||||
- **取值**:邮箱页 `configs`/`senderRules`/`messages`;日志页 `http`/`login`/`app`
|
||||
- **读取**:路由 `/email-bills`、`/logs` 的 `redirect` 读取偏好并拼接子路径(`router/index.ts:69`、第 93 行),带白名单校验,无记录回退默认。
|
||||
- **读取**:路由 `/email-bills`、`/logs` 的 `redirect` 读取偏好并拼接子路径(`router/index.ts`),带白名单校验,无记录回退默认。
|
||||
- **保存**:`watch(activeTab, ..., { immediate: true })` 在 Tab 切换时写入。
|
||||
- **目的**:下次点击「邮箱账单」「系统日志」回到上次标签页。
|
||||
- **说明**:这两个是 `AGENTS.md` 点名的参考实现,但键名未加 `finance.` 前缀,属早期实现。
|
||||
- **目的**:下次点击「邮箱账单」「日志」回到上次标签页。
|
||||
- **说明**:这两个是 `AGENTS.md` 点名的参考实现,早期未加前缀,现已统一为 `ledgerly.` 前缀并做旧键迁移。
|
||||
|
||||
### AI 查询浮窗位置 `ledgerly.transaction.aiQueryWindow` / `ledgerly.dashboard.aiQueryWindow`
|
||||
|
||||
- **文件**:`components/FloatingPanel.vue`(键经 `storageKey` 传入,由 `AiQueryWindow.vue` 复用)
|
||||
- **取值**:`{ x, y, width, height }`,非法值回退居中与默认尺寸(默认 560 × 360)。
|
||||
- **保存**:拖拽或缩放结束时写入;读取在浮窗初始化时。
|
||||
- **目的**:记住 AI 查询浮窗上次的位置与大小,下次打开保持原样。
|
||||
|
||||
### 各表格每页条数 `ledgerly.pagination.tablePageSizes`
|
||||
|
||||
- **文件**:`frontend/src/utils/tablePageSizePrefs.ts`、`composables/useTablePageSize.ts`
|
||||
- **取值**:`Record<tableKey, number>`,按表格键(`transaction`、`categoryExpense`、`rule`、`logsHttp`、`smart-query` 等)分别记录。
|
||||
- **优先级**:本机记录(且须在当前分页候选中)> 全局默认值(后端 `paginationPrefs`)> 20。
|
||||
- **保存**:切换某表格每页条数时写入;候选列表与全局默认值在[设置 → 外观设置](/guide/settings#外观设置)中配置(存后端)。
|
||||
|
||||
## sessionStorage 偏好:滚动位置
|
||||
|
||||
- **文件**:`frontend/src/views/Layout.vue`(前缀第 83 行,键为 `finance.scroll.<路由路径>`)
|
||||
- **文件**:`frontend/src/views/Layout.vue`(前缀第 83 行,键为 `ledgerly.scroll.<路由路径>`)
|
||||
- **保存**:内容区 `scroll` 事件经 `requestAnimationFrame` 节流写入;路由切换前保存离开页;`beforeunload` 兜底(第 172 行)。
|
||||
- **读取**:`restoreScroll()`(第 139 行)在 `onMounted` 与路由切换后恢复;因页面内容异步加载、切换后高度可能不足,用 `rAF` 轮询等待内容高度足够再定位,2 秒超时后按当前可达最大值定位。
|
||||
- **目的**:刷新或页面切换后恢复阅读位置;仅当前会话有效,关闭浏览器即失效。
|
||||
@@ -181,9 +202,33 @@
|
||||
- **前端**:设置页 `components/BillSourceStatsPanel.vue` 编辑保存(可点整行或开关切换)。
|
||||
- **目的**:按账单来源设置导入时默认是否计入统计,仅影响统计口径,不改变导入结果。
|
||||
|
||||
### 显示已隐藏备注的记录
|
||||
|
||||
- **接口**:`GET/PUT /api/setting/show-hidden-data`(`api/setting.ts`)
|
||||
- **数据结构**:`{ show: boolean }`,默认 `false`。
|
||||
- **存储**:`t_user_preference` 的 `pref_key = showHiddenData`,值以 `"true"`/`"false"` 保存。
|
||||
- **前端**:设置页 `components/DisplayConfigPanel.vue` 切换即存;收支列表与 Excel 导出据此过滤(关闭时排除 `remark_hidden = true` 的记录),统计备注搜索不受影响。
|
||||
- **目的**:统一控制「已隐藏备注的记录」是否出现在列表与导出中。
|
||||
|
||||
### 表格分页偏好
|
||||
|
||||
- **接口**:`GET/PUT /api/setting/pagination-prefs`(`api/setting.ts`)
|
||||
- **数据结构**:`{ options: number[], defaultSize: number }`,默认候选 `[10, 20, 50, 100]`、默认 20;后端会规范化(去重升序、1–1000、最多 10 项)。
|
||||
- **存储**:`t_user_preference` 的 `pref_key = paginationPrefs`(JSON)。
|
||||
- **前端**:设置页 `components/DisplayConfigPanel.vue` 编辑、防抖 500ms 自动保存;`composables/usePageSizeOptions.ts` 提供全局单例,`useTablePageSize(tableKey)` 读取各表记忆值。
|
||||
- **目的**:统一所有表格的分页候选与默认每页条数。
|
||||
|
||||
### 导航顺序与显隐
|
||||
|
||||
- **接口**:`GET/PUT /api/setting/nav-menu`(`api/setting.ts`)
|
||||
- **数据结构**:`{ items: [{ key, visible }] }`,未配置时以前端 `utils/navItems.ts` 的默认顺序与可见性为准。
|
||||
- **存储**:`t_user_preference` 的 `pref_key = navMenu`(JSON)。
|
||||
- **前端**:设置页 `components/NavMenuConfigPanel.vue`(`vuedraggable` 拖拽 + 开关,600ms 防抖保存、可恢复默认);`composables/useNavMenu.ts` 与后端合并,`Layout.vue` 据此渲染。隐藏仅移除菜单入口,路由仍可访问。
|
||||
- **目的**:按账号自定义左侧导航的顺序与显隐,跨设备生效。
|
||||
|
||||
### AI 配置
|
||||
|
||||
- **接口**:`GET /api/ai/config`、`PUT /api/ai/config`(`api/ai.ts:37`)
|
||||
- **接口**:`GET /api/ai/config`、`PUT /api/ai/config`(`api/ai.ts`)
|
||||
- **数据结构**:`{ enabled, apiKey, hasApiKey, baseUrl, model, visionModel }`,`apiKey` 留空表示不修改原值。
|
||||
- **前端**:设置页 `components/AiConfigPanel.vue` 编辑保存。
|
||||
- **目的**:用户级 AI 能力配置;其他页面的 AI 元素统一经 `getAiConfig().enabled` 控制显隐。
|
||||
@@ -195,11 +240,10 @@
|
||||
以下常见界面偏好当前**没有**持久化实现,改动时无需假设其存在:
|
||||
|
||||
- **主题 / 暗色模式**:无主题存储键,界面主题固定。
|
||||
- **侧边栏折叠 / 展开**:`Layout.vue` 使用固定 `NLayoutSider`,无折叠状态。
|
||||
- **侧边栏整体折叠 / 展开**:`Layout.vue` 使用固定 `NLayoutSider`,无折叠状态(但菜单项的**顺序与显隐**可配置,见上「导航顺序与显隐」)。
|
||||
- **表格列显示 / 隐藏 / 顺序**:各页列配置为固定 `computed`。
|
||||
- **货币 / 金额格式**:无货币偏好。
|
||||
- **独立日期格式**:仅由语言 `locale` 间接驱动 Naive UI 日期本地化,无单独偏好。
|
||||
- **全局分页大小**:仅收支列表的 `pageSize` 持久化;日志、规则、邮箱账单等页面分页大小不持久化。
|
||||
- **IndexedDB / cookie**:未使用。
|
||||
- **Pinia 持久化插件 / vueuse `useStorage`**:未安装、未使用。
|
||||
|
||||
@@ -214,8 +258,9 @@
|
||||
|
||||
新增一项用户界面偏好时:
|
||||
|
||||
1. 存储键以 `finance.` 前缀命名,按页面/模块分段(如 `finance.transaction.xxx`)。
|
||||
2. 优先用 `watch` 自动保存,避免要求用户手动点保存。
|
||||
3. 读取时对取值做白名单校验,非法或损坏数据回退默认项。
|
||||
4. 写入包 `try/catch`,忽略存储异常。
|
||||
5. 需要跨设备一致时,改存后端并按当前用户隔离,而非 `localStorage`。
|
||||
1. 存储键常量集中登记在 `frontend/src/utils/storageKeys.ts` 的 `STORAGE_KEYS`,以 `ledgerly.` 前缀命名、按页面/模块分段(如 `ledgerly.transaction.xxx`);涉及改名时在 `LEGACY_STORAGE_KEYS` 登记旧键并用 `migrateKey` 迁移。
|
||||
2. 读写统一经 `frontend/src/utils/storage.ts`,**禁止**在业务代码中直接调用 `localStorage`/`sessionStorage`。
|
||||
3. 优先用 `watch` 自动保存,避免要求用户手动点保存。
|
||||
4. 读取时对取值做白名单校验,非法或损坏数据回退默认项;写入包 `try/catch`,忽略存储异常。
|
||||
5. 组件内的字符串型偏好用 `useStringPreference`,关键词历史用 `useKeywordHistory`,浮窗位置尺寸用 `FloatingPanel`(键经 `storageKey` 传入)。
|
||||
6. 需要跨设备一致时,改存后端并按当前用户隔离,而非 `localStorage`。
|
||||
|
||||
@@ -19,7 +19,7 @@ ledgerly/
|
||||
|
||||
## 后端包结构
|
||||
|
||||
`src/main/java/cn/rosercode/finance/`
|
||||
`src/main/java/cn/rosercode/ledgerly/`
|
||||
|
||||
| 包 | 职责 |
|
||||
|---|---|
|
||||
@@ -39,7 +39,8 @@ ledgerly/
|
||||
| `notification` | 通知渠道发送器 |
|
||||
| `scheduler` | 定时任务(通知、日志清理、邮箱账单) |
|
||||
| `service` / `service.impl` | 业务层 |
|
||||
| `util` | 工具类(`JwtUtil`、`UserContext`、`AppLogUtils`、`ImageUtils`、`AppHome`、`TransactionDedupUtils` 等) |
|
||||
| `util` | 工具类(`JwtUtil`、`UserContext`、`AppLogUtils`、`ImageUtils`、`AppHome`、`TransactionDedupUtils` 等);`util.sql` 为智能查数的安全网关(`TextToSqlGuard` / `TextToSqlTables` / `TextToSqlSchemaProvider`) |
|
||||
| `prompt` | AI 提示词模板加载(`PromptTemplateService`,模板位于 `resources/prompts/`) |
|
||||
| `vo` | 响应对象 |
|
||||
| `launcher` | 桌面 GUI 启动器(仅打入带 GUI 的 jar) |
|
||||
|
||||
@@ -67,11 +68,12 @@ App.vue # 应用根组件
|
||||
main.ts # 入口(挂载应用与插件)
|
||||
style.css # 全局样式
|
||||
api/ # 各模块 REST 接口封装(axios)
|
||||
components/ # 复用组件(账单预览、导入结果、AI/通知/数据库配置面板等)
|
||||
components/ # 复用组件(账单预览、导入结果、AI/通知/数据库配置面板、账单关系、AI 查询浮窗等)
|
||||
composables/ # 组合式函数(AI 启用状态、导航配置、分页选项、关键词历史等)
|
||||
locales/ # 中/英/日三语文案(messages.ts)
|
||||
router/ # 路由与导航守卫
|
||||
store/ # Pinia 状态(user / database / registration)
|
||||
utils/ # 工具(交易渠道标签、SSE 封装、渲染辅助)
|
||||
utils/ # 工具(交易渠道标签、SSE 封装、本地偏好存储、渲染辅助)
|
||||
views/ # 页面组件
|
||||
```
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
- `GET /api/rule/list`:返回当前用户全部规则(按优先级排序),供内部复用。
|
||||
- `GET /api/rule/page`:分页查询,支持 `keyword`(匹配规则名称或关键词)、`enabled`、`type` 筛选,返回 `PageResult<RuleVO>`。
|
||||
- `DELETE /api/rule/batch`:批量删除(body `{ ids }`);先按 `userId` 过滤归属,清理规则-标签关联后删除,返回实际删除数。
|
||||
|
||||
## AI 生成规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user