fix
This commit is contained in:
@@ -175,25 +175,24 @@ public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository<Tran
|
||||
var query = FreeSql.Select<TransactionRecord>();
|
||||
|
||||
// 如果提供了搜索关键词,则添加搜索条件
|
||||
if (!string.IsNullOrWhiteSpace(searchKeyword))
|
||||
{
|
||||
query = query.Where(t => t.Reason.Contains(searchKeyword) ||
|
||||
t.Classify.Contains(searchKeyword) ||
|
||||
t.Card.Contains(searchKeyword) ||
|
||||
t.ImportFrom.Contains(searchKeyword));
|
||||
}
|
||||
query = query.WhereIf(!string.IsNullOrWhiteSpace(searchKeyword),
|
||||
t => t.Reason.Contains(searchKeyword!) ||
|
||||
t.Classify.Contains(searchKeyword!) ||
|
||||
t.Card.Contains(searchKeyword!) ||
|
||||
t.ImportFrom.Contains(searchKeyword!));
|
||||
|
||||
// 按分类筛选
|
||||
if (!string.IsNullOrWhiteSpace(classify))
|
||||
{
|
||||
if (classify == "未分类")
|
||||
{
|
||||
classify = string.Empty;
|
||||
}
|
||||
query = query.Where(t => t.Classify == classify);
|
||||
}
|
||||
|
||||
// 按交易类型筛选
|
||||
if (type.HasValue)
|
||||
{
|
||||
query = query.Where(t => t.Type == type.Value);
|
||||
}
|
||||
query = query.WhereIf(type.HasValue, t => t.Type == type!.Value);
|
||||
|
||||
// 按年月筛选
|
||||
if (year.HasValue && month.HasValue)
|
||||
|
||||
@@ -341,7 +341,7 @@ public class ImportService(
|
||||
}
|
||||
|
||||
// 使用正则表达式提取退款金额
|
||||
var regex = new System.Text.RegularExpressions.Regex(@"¥(-?\d+(\.\d+)?)");
|
||||
var regex = new Regex(@"¥(-?\d+(\.\d+)?)");
|
||||
var match = regex.Match(status);
|
||||
if (match.Success && decimal.TryParse(match.Groups[1].Value, out var refundAmount))
|
||||
{
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
<div class="app-root">
|
||||
<RouterView />
|
||||
<van-tabbar v-model="active" v-show="showTabbar">
|
||||
<van-tabbar-item icon="notes" to="/calendar">
|
||||
<van-tabbar-item name="ccalendar" icon="notes" to="/calendar">
|
||||
日历
|
||||
</van-tabbar-item>
|
||||
<van-tabbar-item icon="chart-trending-o" to="/statistics">
|
||||
<van-tabbar-item name="statistics" icon="chart-trending-o" to="/statistics">
|
||||
统计
|
||||
</van-tabbar-item>
|
||||
<van-tabbar-item icon="balance-list" to="/" @click="handleTabClick('/')">
|
||||
<van-tabbar-item name="balance" icon="balance-list" to="/" @click="handleTabClick('/')">
|
||||
账单
|
||||
</van-tabbar-item>
|
||||
<van-tabbar-item icon="records" to="/email" @click="handleTabClick('/email')">
|
||||
<van-tabbar-item name="email" icon="records" to="/email" @click="handleTabClick('/email')">
|
||||
邮件
|
||||
</van-tabbar-item>
|
||||
<van-tabbar-item icon="setting" to="/setting">
|
||||
<van-tabbar-item name="setting" icon="setting" to="/setting">
|
||||
设置
|
||||
</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<script setup>
|
||||
import { RouterView, useRoute } from 'vue-router'
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
|
||||
import '@/styles/common.css'
|
||||
|
||||
const updateVh = () => {
|
||||
@@ -61,7 +61,7 @@ const showTabbar = computed(() => {
|
||||
route.path === '/statistics'
|
||||
})
|
||||
|
||||
const active = ref(0)
|
||||
const active = ref('')
|
||||
const theme = ref('light')
|
||||
|
||||
// 检测系统深色模式
|
||||
@@ -76,8 +76,32 @@ onMounted(() => {
|
||||
updateTheme()
|
||||
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
mediaQuery.addEventListener('change', updateTheme)
|
||||
setActive(route.path)
|
||||
})
|
||||
|
||||
// 监听路由变化调整
|
||||
watch(() => route.path, (newPath) => {
|
||||
setActive(newPath)
|
||||
})
|
||||
|
||||
const setActive = (path) => {
|
||||
active.value = (() => {
|
||||
switch (path) {
|
||||
case '/calendar':
|
||||
return 'ccalendar'
|
||||
case '/statistics':
|
||||
return 'statistics'
|
||||
case '/email':
|
||||
return 'email'
|
||||
case '/setting':
|
||||
return 'setting'
|
||||
default:
|
||||
return 'balance'
|
||||
}
|
||||
})()
|
||||
console.log(active.value, path)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (mediaQuery) {
|
||||
mediaQuery.removeEventListener('change', updateTheme)
|
||||
|
||||
@@ -152,3 +152,12 @@
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* 账单列表滚动容器 */
|
||||
.bills-scroll-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<p v-if="dateTransactions.length">共 {{ dateTransactions.length }} 笔交易</p>
|
||||
</div>
|
||||
|
||||
<div class="bills-scroll-container">
|
||||
<TransactionList
|
||||
:transactions="dateTransactions"
|
||||
:loading="listLoading"
|
||||
@@ -33,6 +34,7 @@
|
||||
@click="viewDetail"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<!-- 交易详情组件 -->
|
||||
|
||||
@@ -141,13 +141,15 @@
|
||||
<div class="list-header">
|
||||
<h3 style="margin: 16px;">关联账单列表</h3>
|
||||
</div>
|
||||
<TransactionList
|
||||
:transactions="transactionList"
|
||||
:loading="false"
|
||||
:finished="true"
|
||||
:show-delete="false"
|
||||
@click="handleTransactionClick"
|
||||
/>
|
||||
<div class="bills-scroll-container">
|
||||
<TransactionList
|
||||
:transactions="transactionList"
|
||||
:loading="false"
|
||||
:finished="true"
|
||||
:show-delete="false"
|
||||
@click="handleTransactionClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
@@ -434,7 +436,13 @@ onMounted(() => {
|
||||
|
||||
.transaction-list-popup {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.list-header{
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
}
|
||||
|
||||
.email-content {
|
||||
|
||||
@@ -562,7 +562,7 @@ const goToAnalysis = () => {
|
||||
|
||||
// 打开分类账单列表
|
||||
const goToCategoryBills = (classify, type) => {
|
||||
selectedClassify.value = classify || ''
|
||||
selectedClassify.value = classify || '未分类'
|
||||
selectedType.value = type
|
||||
selectedCategoryTitle.value = `${classify || '未分类'} - ${type === 0 ? '支出' : '收入'}`
|
||||
|
||||
@@ -1076,14 +1076,6 @@ onActivated(() => {
|
||||
color: var(--van-text-color-2);
|
||||
}
|
||||
|
||||
/* 账单列表滚动容器 */
|
||||
.bills-scroll-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* 修复深色模式下van组件的背景色 */
|
||||
.category-bills :deep(.van-list) {
|
||||
background: transparent;
|
||||
|
||||
@@ -15,7 +15,7 @@ public class TransactionRecordController(
|
||||
/// 获取交易记录列表(分页)
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<PagedResponse<Entity.TransactionRecord>> GetListAsync(
|
||||
public async Task<PagedResponse<TransactionRecord>> GetListAsync(
|
||||
[FromQuery] DateTime? lastOccurredAt = null,
|
||||
[FromQuery] long? lastId = null,
|
||||
[FromQuery] string? searchKeyword = null,
|
||||
@@ -39,7 +39,7 @@ public class TransactionRecordController(
|
||||
month);
|
||||
var total = await transactionRepository.GetTotalCountAsync();
|
||||
|
||||
return new PagedResponse<Entity.TransactionRecord>
|
||||
return new PagedResponse<TransactionRecord>
|
||||
{
|
||||
Success = true,
|
||||
Data = list.ToArray(),
|
||||
@@ -51,7 +51,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取交易记录列表失败,时间: {LastTime}, ID: {LastId}", lastOccurredAt, lastId);
|
||||
return PagedResponse<Entity.TransactionRecord>.Fail($"获取交易记录列表失败: {ex.Message}");
|
||||
return PagedResponse<TransactionRecord>.Fail($"获取交易记录列表失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,17 +59,17 @@ public class TransactionRecordController(
|
||||
/// 根据ID获取交易记录详情
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<BaseResponse<Entity.TransactionRecord>> GetByIdAsync(long id)
|
||||
public async Task<BaseResponse<TransactionRecord>> GetByIdAsync(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = await transactionRepository.GetByIdAsync(id);
|
||||
if (transaction == null)
|
||||
{
|
||||
return BaseResponse<Entity.TransactionRecord>.Fail("交易记录不存在");
|
||||
return BaseResponse<TransactionRecord>.Fail("交易记录不存在");
|
||||
}
|
||||
|
||||
return new BaseResponse<Entity.TransactionRecord>
|
||||
return new BaseResponse<TransactionRecord>
|
||||
{
|
||||
Success = true,
|
||||
Data = transaction
|
||||
@@ -78,7 +78,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取交易记录详情失败,交易ID: {TransactionId}", id);
|
||||
return BaseResponse<Entity.TransactionRecord>.Fail($"获取交易记录详情失败: {ex.Message}");
|
||||
return BaseResponse<TransactionRecord>.Fail($"获取交易记录详情失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ public class TransactionRecordController(
|
||||
/// 根据邮件ID获取交易记录列表
|
||||
/// </summary>
|
||||
[HttpGet("{emailId}")]
|
||||
public async Task<BaseResponse<List<Entity.TransactionRecord>>> GetByEmailIdAsync(long emailId)
|
||||
public async Task<BaseResponse<List<TransactionRecord>>> GetByEmailIdAsync(long emailId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transactions = await transactionRepository.GetByEmailIdAsync(emailId);
|
||||
return new BaseResponse<List<Entity.TransactionRecord>>
|
||||
return new BaseResponse<List<TransactionRecord>>
|
||||
{
|
||||
Success = true,
|
||||
Data = transactions
|
||||
@@ -100,7 +100,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取邮件交易记录失败,邮件ID: {EmailId}", emailId);
|
||||
return BaseResponse<List<Entity.TransactionRecord>>.Fail($"获取邮件交易记录失败: {ex.Message}");
|
||||
return BaseResponse<List<TransactionRecord>>.Fail($"获取邮件交易记录失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class TransactionRecordController(
|
||||
return BaseResponse.Fail("交易时间格式不正确");
|
||||
}
|
||||
|
||||
var transaction = new Entity.TransactionRecord
|
||||
var transaction = new TransactionRecord
|
||||
{
|
||||
OccurredAt = occurredAt,
|
||||
Reason = dto.Reason ?? string.Empty,
|
||||
@@ -416,7 +416,7 @@ public class TransactionRecordController(
|
||||
}
|
||||
|
||||
// 第三步:将查询结果序列化为JSON,直接传递给AI生成分析报告
|
||||
var dataJson = System.Text.Json.JsonSerializer.Serialize(queryResults, new System.Text.Json.JsonSerializerOptions
|
||||
var dataJson = System.Text.Json.JsonSerializer.Serialize(queryResults, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
@@ -482,13 +482,13 @@ public class TransactionRecordController(
|
||||
/// 获取指定日期的交易记录
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<BaseResponse<List<Entity.TransactionRecord>>> GetByDateAsync([FromQuery] string date)
|
||||
public async Task<BaseResponse<List<TransactionRecord>>> GetByDateAsync([FromQuery] string date)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!DateTime.TryParse(date, out var targetDate))
|
||||
{
|
||||
return BaseResponse<List<Entity.TransactionRecord>>.Fail("日期格式不正确");
|
||||
return BaseResponse<List<TransactionRecord>>.Fail("日期格式不正确");
|
||||
}
|
||||
|
||||
// 获取当天的开始和结束时间
|
||||
@@ -497,7 +497,7 @@ public class TransactionRecordController(
|
||||
|
||||
var records = await transactionRepository.GetByDateRangeAsync(startDate, endDate);
|
||||
|
||||
return new BaseResponse<List<Entity.TransactionRecord>>
|
||||
return new BaseResponse<List<TransactionRecord>>
|
||||
{
|
||||
Success = true,
|
||||
Data = records
|
||||
@@ -506,7 +506,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取指定日期的交易记录失败,日期: {Date}", date);
|
||||
return BaseResponse<List<Entity.TransactionRecord>>.Fail($"获取指定日期的交易记录失败: {ex.Message}");
|
||||
return BaseResponse<List<TransactionRecord>>.Fail($"获取指定日期的交易记录失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,12 +536,12 @@ public class TransactionRecordController(
|
||||
/// 获取未分类的账单列表
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<BaseResponse<List<Entity.TransactionRecord>>> GetUnclassifiedAsync([FromQuery] int pageSize = 10)
|
||||
public async Task<BaseResponse<List<TransactionRecord>>> GetUnclassifiedAsync([FromQuery] int pageSize = 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
var records = await transactionRepository.GetUnclassifiedAsync(pageSize);
|
||||
return new BaseResponse<List<Entity.TransactionRecord>>
|
||||
return new BaseResponse<List<TransactionRecord>>
|
||||
{
|
||||
Success = true,
|
||||
Data = records
|
||||
@@ -550,7 +550,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取未分类账单列表失败");
|
||||
return BaseResponse<List<Entity.TransactionRecord>>.Fail($"获取未分类账单列表失败: {ex.Message}");
|
||||
return BaseResponse<List<TransactionRecord>>.Fail($"获取未分类账单列表失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +574,7 @@ public class TransactionRecordController(
|
||||
}
|
||||
|
||||
// 获取指定ID的账单
|
||||
var records = new List<Entity.TransactionRecord>();
|
||||
var records = new List<TransactionRecord>();
|
||||
foreach (var id in request.TransactionIds)
|
||||
{
|
||||
var record = await transactionRepository.GetByIdAsync(id);
|
||||
@@ -702,14 +702,14 @@ public class TransactionRecordController(
|
||||
/// 获取按交易摘要分组的统计信息(支持分页)
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<PagedResponse<Repository.ReasonGroupDto>> GetReasonGroupsAsync(
|
||||
public async Task<PagedResponse<ReasonGroupDto>> GetReasonGroupsAsync(
|
||||
[FromQuery] int pageIndex = 1,
|
||||
[FromQuery] int pageSize = 20)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (list, total) = await transactionRepository.GetReasonGroupsAsync(pageIndex, pageSize);
|
||||
return new PagedResponse<Repository.ReasonGroupDto>
|
||||
return new PagedResponse<ReasonGroupDto>
|
||||
{
|
||||
Success = true,
|
||||
Data = list.ToArray(),
|
||||
@@ -719,7 +719,7 @@ public class TransactionRecordController(
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "获取交易摘要分组失败");
|
||||
return PagedResponse<Repository.ReasonGroupDto>.Fail($"获取交易摘要分组失败: {ex.Message}");
|
||||
return PagedResponse<ReasonGroupDto>.Fail($"获取交易摘要分组失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,21 +780,33 @@ public class TransactionRecordController(
|
||||
{{categoryInfo}}
|
||||
|
||||
你需要分析用户的需求,提取以下信息:
|
||||
1. 查询SQL, 根据用户的描述生成SQL Where 子句,用于查询交易记录。例如:Reason LIKE '%关键词%'
|
||||
Table Schema:
|
||||
1. 查询SQL, 根据用户的描述生成完整SQL语句,用于查询交易记录。例如:SELECT * FROM TransactionRecord WHERE Reason LIKE '%关键词%' OR Classify LIKE '%关键词2%' LIMIT 500
|
||||
[重要Table Schema:]
|
||||
```
|
||||
TransactionRecord (
|
||||
Id LONG,
|
||||
Reason STRING,
|
||||
Reason STRING NOT NULL,
|
||||
Amount DECIMAL,
|
||||
RefundAmount DECIMAL,
|
||||
Balance DECIMAL,
|
||||
OccurredAt DATETIME,
|
||||
EmailMessageId LONG,
|
||||
Type INT,
|
||||
Classify STRING,
|
||||
ImportNo STRING,
|
||||
ImportFrom STRING
|
||||
Classify STRING NOT NULL,
|
||||
ImportNo STRING NOT NULL,
|
||||
ImportFrom STRING NOT NULL
|
||||
)
|
||||
```
|
||||
[重要]
|
||||
如果用户没有限制,则最多查询500条记录;如果用户指定了时间范围,请在SQL中加入时间过滤条件。
|
||||
[重要SQL限制]
|
||||
必须是SELECT * FROM TransactionRecord 开头的SQL语句。
|
||||
当前日期:{{DateTime.Now:yyyy年M月d日}}
|
||||
[重要SQLite日期函数]
|
||||
- 提取年份:strftime('%Y', OccurredAt)
|
||||
- 提取月份:strftime('%m', OccurredAt)
|
||||
- 提取日期:strftime('%Y-%m-%d', OccurredAt)
|
||||
- 不要使用 YEAR()、MONTH()、DAY() 函数,SQLite不支持
|
||||
2. 目标交易类型(0:支出, 1:收入, 2:不计入收支)
|
||||
3. 目标分类名称(必须从上面的分类列表中选择)
|
||||
|
||||
@@ -835,7 +847,7 @@ public class TransactionRecordController(
|
||||
}
|
||||
|
||||
// 根据关键词查询交易记录
|
||||
var allRecords = await transactionRepository.QueryByWhereAsync(analysisInfo.Sql);
|
||||
var allRecords = await transactionRepository.ExecuteRawSqlAsync(analysisInfo.Sql);
|
||||
logger.LogInformation("NLP分析查询到 {Count} 条记录,SQL: {Sql}", allRecords.Count, analysisInfo.Sql);
|
||||
|
||||
// 为每条记录预设分类
|
||||
|
||||
Reference in New Issue
Block a user