feat: 新增智能查数(自然语言转 SQL 只读查询)
新增「智能查数」功能:用户用自然语言提问,由大模型生成一条只读 SQL,经安全校验后在当前用户数据上执行,前端按结果形状自适应渲染为统计卡片、表格或图表。入口位于侧边栏,仅在 AI 启用时显示。 变更内容: - 后端新增 AiSqlQueryService 与 POST /api/ai/sql,新增 AiCallType.SQL 用量记录与提示词模板 ai-sql-system.tpl - 新增安全网关 TextToSqlGuard:仅允许单条 SELECT,拒绝写操作、多语句、危险函数与 SELECT INTO/FOR UPDATE;校验表白名单与 schema 前缀;递归注入 user_id 隔离(主表加 WHERE、JOIN 加 ON,覆盖括号连接与 CTE);结果上限 1000 - 表结构改由 TextToSqlSchemaProvider 从数据库元数据动态读取(60 秒缓存),白名单与表列中文说明集中在 TextToSqlTables,字段增删改无需调整提示词 - 新增 AiSqlQueryResultVO、AiSqlColumnVO,返回生成的 SQL、列元数据与行数据 - 前端新增智能查数页 SqlQuery.vue(路由 /smart-query),按结果形状渲染单行统计卡片、多行动态表格并支持柱状图与饼图切换 - 渠道列用 getChannelLabel 本地化展示,生成的 SQL 用 sql-formatter 缩进、highlight.js 高亮并支持复制 - 新增共享 useAiEnabled 状态,侧边栏「智能查数」菜单仅 AI 启用时显示,设置页保存 AI 配置后即时刷新 - 设置页与收支记录页文案补充智能查数相关三语文案 依赖与清理: - 新增前端依赖 sql-formatter、highlight.js - 移除仓库内测试文件 LedgerlyApplicationTests、PromptTemplateServiceTest、AiPromptPayloadPrivacyTest,此后测试源码不再纳入提交 说明: - 表结构动态读取保证字段变更零维护,仅可查表白名单需显式登记 - 安全网关对 JOIN 条件注入 ON 子句以保证 LEFT JOIN 语义,并对括号连接与 CTE 递归处理
This commit is contained in:
@@ -93,6 +93,7 @@ export default {
|
||||
tags: { zh: '标签管理', en: 'Tags', ja: 'タグ管理' },
|
||||
rules: { zh: '规则管理', en: 'Rules', ja: 'ルール管理' },
|
||||
emailBills: { zh: '邮箱账单', en: 'Email Bills', ja: 'メール請求' },
|
||||
sqlQuery: { zh: '智能查数', en: 'Smart Data', ja: 'スマート集計' },
|
||||
logs: { zh: '日志', en: 'Logs', ja: 'ログ' },
|
||||
settings: { zh: '设置', en: 'Settings', ja: '設定' },
|
||||
user: { zh: '用户', en: 'User', ja: 'ユーザー' },
|
||||
@@ -845,6 +846,31 @@ export default {
|
||||
includeNo: { zh: '不计入', en: 'Excluded', ja: '含めない' },
|
||||
unmatched: { zh: '未识别', en: 'Unmatched', ja: '未認識' },
|
||||
},
|
||||
sql: {
|
||||
entry: { zh: '智能查数', en: 'Smart Data', ja: 'スマート集計' },
|
||||
title: { zh: '智能查数', en: 'Smart Data Query', ja: 'スマート集計' },
|
||||
hint: { zh: '用自然语言描述你想查的内容,AI 会生成只读查询并展示结果;只能查询你本人的数据。', en: 'Describe what you want to query; AI generates a read-only query and shows the result. Only your own data can be queried.', ja: '知りたい内容を自然言語で説明すると、AI が読み取り専用クエリを生成して結果を表示します。自分のデータのみ参照できます。' },
|
||||
placeholder: { zh: '例如:上个月一共花了多少钱', en: 'e.g. How much did I spend last month in total?', ja: '例:先月の支出合計はいくら?' },
|
||||
examples: { zh: '上个月一共花了多少钱|按分类统计今年的支出|今年收入最高的一个月|各渠道的支出占比|本月支出最高的5笔', en: 'How much did I spend last month?|Expenses by category this year|Month with highest income this year|Spending share by channel|Top 5 expenses this month', ja: '先月の支出合計|今年のカテゴリ別支出|今年の収入が最も多い月|チャネル別支出の割合|今月の支出トップ5' },
|
||||
submit: { zh: '查询', en: 'Query', ja: '検索' },
|
||||
submitting: { zh: '正在查询,请稍候…', en: 'Querying, please wait…', ja: '検索中です。しばらくお待ちください…' },
|
||||
emptyInput: { zh: '请输入查询内容', en: 'Please enter a query', ja: '検索内容を入力してください' },
|
||||
failed: { zh: '智能查数失败', en: 'Smart data query failed', ja: 'スマート集計に失敗しました' },
|
||||
resultTitle: { zh: '查询结果', en: 'Result', ja: '結果' },
|
||||
sqlTitle: { zh: '生成的 SQL', en: 'Generated SQL', ja: '生成された SQL' },
|
||||
copySql: { zh: '复制 SQL', en: 'Copy SQL', ja: 'SQL をコピー' },
|
||||
copySuccess: { zh: '已复制', en: 'Copied', ja: 'コピーしました' },
|
||||
empty: { zh: '查询没有返回结果', en: 'The query returned no result', ja: '結果がありません' },
|
||||
rowCount: { zh: '共 {count} 行', en: '{count} row(s)', ja: '{count} 行' },
|
||||
truncated: { zh: '结果超过 {max} 行,仅展示前 {max} 行', en: 'More than {max} rows; only the first {max} are shown', ja: '{max} 行を超えるため、先頭 {max} 行のみ表示します' },
|
||||
viewTable: { zh: '表格', en: 'Table', ja: 'テーブル' },
|
||||
viewBar: { zh: '柱状图', en: 'Bar chart', ja: '棒グラフ' },
|
||||
viewPie: { zh: '饼图', en: 'Pie chart', ja: '円グラフ' },
|
||||
examplesLabel: { zh: '示例', en: 'Examples', ja: '例' },
|
||||
history: { zh: '历史提问', en: 'History', ja: '履歴' },
|
||||
clearHistory: { zh: '清空历史', en: 'Clear history', ja: '履歴をクリア' },
|
||||
needConfig: { zh: 'AI 未启用,请先在「设置」中开启 AI。', en: 'AI is not enabled. Please enable it in Settings first.', ja: 'AI が有効になっていません。「設定」で有効にしてください。' },
|
||||
},
|
||||
record: {
|
||||
button: { zh: '智能记账', en: 'Smart Entry', ja: 'スマート入力' },
|
||||
title: { zh: 'AI 智能记账', en: 'AI Smart Entry', ja: 'AI スマート入力' },
|
||||
@@ -923,6 +949,7 @@ export default {
|
||||
rule: { zh: '生成规则', en: 'Rule', ja: 'ルール生成' },
|
||||
statistics: { zh: '统计搜索', en: 'Statistics', ja: '統計検索' },
|
||||
relation: { zh: '生成关系', en: 'Relation', ja: '関連生成' },
|
||||
sql: { zh: '智能查数', en: 'Smart Data', ja: 'スマート集計' },
|
||||
},
|
||||
column: {
|
||||
time: { zh: '时间', en: 'Time', ja: '日時' },
|
||||
|
||||
Reference in New Issue
Block a user