feat: 交易渠道重命名、账单来源统计默认值、AI 规则生成与统计搜索,AI 改用 openai-java SDK
- 交易记录「支付方式」全链路重命名为「交易渠道」(channel):DB 列、实体/DTO/VO、接口 /api/transaction/channels、统计维度 CHANNEL、AI 提示词与 JSON 字段、前端与中英日文案;新增 SchemaMigrator 在启动/切换数据源时幂等迁移列名(MySQL 用 information_schema、SQLite 用 PRAGMA) - 按账单来源设置「计入统计」默认值:AppConfig 持久化 + GET/PUT /api/app/bill-source-include-in-stats,导入解析按「解析器判定 && 来源默认」应用并覆盖上传与邮箱两条路径,设置页新增面板 - AI 生成规则:基于所选已打标签记录归纳分类/标签规则(POST /api/ai/rule/suggest、/api/ai/rule/apply),规则页两步弹窗(选择记录→预览创建),支持候选记录分页/搜索/筛选;按「类型+关键词+分类+标签」签名与已有规则去重,重复项标记「已存在」并禁止导入 - 规则列表新增分页与搜索:GET /api/rule/page(名称/关键词、启用状态、收支类型) - 统计面板 AI 搜索:POST /api/ai/statistics 将自然语言解析为时间/类型/分类/标签/渠道/金额/统计口径/趋势粒度并回填;交互对齐账单列表 AI 查询(弹窗 + 历史 + 示例 + 空条件守卫);统计后端补充标签筛选,仪表盘新增标签筛选与 AI 搜索 - AI 客户端改用 OpenAI 官方 Java SDK(com.openai:openai-java),自定义 baseUrl 指向 DeepSeek,保留 chat 签名与 DeepSeekResponse - AI 数据外发收敛:分类/标签建议与规则生成不再发送 rawData 与金额,仅发送类型/备注/渠道/分类/标签;新增 AiCallType.RULE / STATISTICS - 收支记录页新增「刷新」按钮
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
// 账单来源统计默认值面板:按账单来源配置导入时「是否计入统计」的默认值
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
useMessage, NCard, NSpace, NSwitch, NButton, NIcon, NText,
|
||||
} from 'naive-ui'
|
||||
import { OptionsOutline, SaveOutline } from '@vicons/ionicons5'
|
||||
import { getBillSourceIncludeInStats, updateBillSourceIncludeInStats } from '../api/app'
|
||||
import { getChannelLabel } from '../utils/channel'
|
||||
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
|
||||
const defaults = ref<Record<string, boolean>>({})
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
|
||||
/** 加载各账单来源的默认值并回显 */
|
||||
async function loadDefaults() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await getBillSourceIncludeInStats()
|
||||
defaults.value = { ...(res.data || {}) }
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('billSourceStats.loadFailed'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存各账单来源的默认值 */
|
||||
async function handleSave() {
|
||||
saving.value = true
|
||||
try {
|
||||
const res: any = await updateBillSourceIncludeInStats(defaults.value)
|
||||
defaults.value = { ...(res.data || {}) }
|
||||
message.success(t('billSourceStats.saveSuccess'))
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('billSourceStats.saveFailed'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadDefaults)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" class="config-card">
|
||||
<template #header>
|
||||
<NSpace align="center" :size="8">
|
||||
<NIcon :component="OptionsOutline" />
|
||||
<span>{{ t('settings.billSourceStatsSection') }}</span>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<NText depth="3" style="font-size: 12px;">{{ t('billSourceStats.hint') }}</NText>
|
||||
|
||||
<div class="source-list">
|
||||
<div v-for="(value, code) in defaults" :key="code" class="source-row">
|
||||
<NSpace align="center" :size="12">
|
||||
<NSwitch v-model:value="defaults[code]" :disabled="loading" />
|
||||
<span>{{ getChannelLabel(String(code)) }}</span>
|
||||
<NText depth="3" style="font-size: 12px;">
|
||||
{{ value ? t('billSourceStats.included') : t('billSourceStats.excluded') }}
|
||||
</NText>
|
||||
</NSpace>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NSpace justify="end" style="margin-top: 16px; width: 100%;">
|
||||
<NButton type="primary" :loading="saving" @click="handleSave">
|
||||
<template #icon>
|
||||
<NIcon :component="SaveOutline" />
|
||||
</template>
|
||||
{{ t('billSourceStats.save') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.source-list {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user