namespace Repository; public interface ITransactionRecordRepository : IBaseRepository { Task ExistsByEmailMessageIdAsync(long emailMessageId, DateTime occurredAt); Task ExistsByImportNoAsync(string importNo, string importFrom); Task> QueryAsync( int? year = null, int? month = null, DateTime? startDate = null, DateTime? endDate = null, TransactionType? type = null, string[]? classifies = null, string? searchKeyword = null, string? reason = null, int pageIndex = 1, int pageSize = int.MaxValue, bool sortByAmount = false); Task CountAsync( int? year = null, int? month = null, DateTime? startDate = null, DateTime? endDate = null, TransactionType? type = null, string[]? classifies = null, string? searchKeyword = null, string? reason = null); Task> GetDistinctClassifyAsync(); Task> GetByEmailIdAsync(long emailMessageId); Task GetCountByEmailIdAsync(long emailMessageId); Task> GetUnclassifiedAsync(int pageSize = 10); Task> GetClassifiedByKeywordsAsync(List keywords, int limit = 10); Task> GetUnconfirmedRecordsAsync(); Task BatchUpdateByReasonAsync(string reason, TransactionType type, string classify); Task UpdateCategoryNameAsync(string oldName, string newName, TransactionType type); Task ConfirmAllUnconfirmedAsync(long[] ids); } public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository(freeSql), ITransactionRecordRepository { private ISelect BuildQuery( int? year = null, int? month = null, DateTime? startDate = null, DateTime? endDate = null, TransactionType? type = null, string[]? classifies = null, string? searchKeyword = null, string? reason = null) { var query = FreeSql.Select(); query = query.WhereIf(!string.IsNullOrWhiteSpace(searchKeyword), t => t.Reason.Contains(searchKeyword!) || t.Classify.Contains(searchKeyword!) || t.Card.Contains(searchKeyword!) || t.ImportFrom.Contains(searchKeyword!)) .WhereIf(!string.IsNullOrWhiteSpace(reason), t => t.Reason == reason); if (classifies is { Length: > 0 }) { var filterClassifies = classifies.Select(c => c == "未分类" ? string.Empty : c).ToList(); query = query.Where(t => filterClassifies.Contains(t.Classify)); } query = query.WhereIf(type.HasValue, t => t.Type == type!.Value); if (year.HasValue) { if (month.HasValue && month.Value > 0) { // 查询指定年月 var dateStart = new DateTime(year.Value, month.Value, 1); var dateEnd = dateStart.AddMonths(1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } else { // 查询整年数据(1月1日到下年1月1日) var dateStart = new DateTime(year.Value, 1, 1); var dateEnd = new DateTime(year.Value + 1, 1, 1); query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd); } } query = query.WhereIf(startDate.HasValue, t => t.OccurredAt >= startDate!.Value) .WhereIf(endDate.HasValue, t => t.OccurredAt <= endDate!.Value); return query; } public async Task ExistsByEmailMessageIdAsync(long emailMessageId, DateTime occurredAt) { return await FreeSql.Select() .Where(t => t.EmailMessageId == emailMessageId && t.OccurredAt == occurredAt) .FirstAsync(); } public async Task ExistsByImportNoAsync(string importNo, string importFrom) { return await FreeSql.Select() .Where(t => t.ImportNo == importNo && t.ImportFrom == importFrom) .FirstAsync(); } public async Task> QueryAsync( int? year = null, int? month = null, DateTime? startDate = null, DateTime? endDate = null, TransactionType? type = null, string[]? classifies = null, string? searchKeyword = null, string? reason = null, int pageIndex = 1, int pageSize = int.MaxValue, bool sortByAmount = false) { var query = BuildQuery(year, month, startDate, endDate, type, classifies, searchKeyword, reason); if (sortByAmount) { return await query .OrderByDescending(t => t.Amount) .OrderByDescending(t => t.Id) .Page(pageIndex, pageSize) .ToListAsync(); } return await query .OrderByDescending(t => t.OccurredAt) .OrderByDescending(t => t.Id) .Page(pageIndex, pageSize) .ToListAsync(); } public async Task CountAsync( int? year = null, int? month = null, DateTime? startDate = null, DateTime? endDate = null, TransactionType? type = null, string[]? classifies = null, string? searchKeyword = null, string? reason = null) { var query = BuildQuery(year, month, startDate, endDate, type, classifies, searchKeyword, reason); return await query.CountAsync(); } public async Task> GetDistinctClassifyAsync() { return await FreeSql.Select() .Where(t => !string.IsNullOrEmpty(t.Classify)) .Distinct() .ToListAsync(t => t.Classify); } public async Task> GetByEmailIdAsync(long emailMessageId) { return await FreeSql.Select() .Where(t => t.EmailMessageId == emailMessageId) .OrderBy(t => t.OccurredAt) .ToListAsync(); } public async Task GetCountByEmailIdAsync(long emailMessageId) { return (int)await FreeSql.Select() .Where(t => t.EmailMessageId == emailMessageId) .CountAsync(); } public async Task> GetUnclassifiedAsync(int pageSize = 10) { return await FreeSql.Select() .Where(t => string.IsNullOrEmpty(t.Classify)) .OrderByDescending(t => t.OccurredAt) .Page(1, pageSize) .ToListAsync(); } public async Task> GetClassifiedByKeywordsAsync(List keywords, int limit = 10) { if (keywords.Count == 0) { return []; } var query = FreeSql.Select() .Where(t => t.Classify != ""); if (keywords.Count > 0) { query = query.Where(t => keywords.Any(keyword => t.Reason.Contains(keyword))); } return await query .OrderByDescending(t => t.OccurredAt) .Limit(limit) .ToListAsync(); } public async Task> GetUnconfirmedRecordsAsync() { return await FreeSql.Select() .Where(t => t.UnconfirmedClassify != null && t.UnconfirmedClassify != "") .OrderByDescending(t => t.OccurredAt) .ToListAsync(); } public async Task BatchUpdateByReasonAsync(string reason, TransactionType type, string classify) { return await FreeSql.Update() .Set(t => t.Type, type) .Set(t => t.Classify, classify) .Where(t => t.Reason == reason) .ExecuteAffrowsAsync(); } public async Task UpdateCategoryNameAsync(string oldName, string newName, TransactionType type) { return await FreeSql.Update() .Set(a => a.Classify, newName) .Where(a => a.Classify == oldName && a.Type == type) .ExecuteAffrowsAsync(); } public async Task ConfirmAllUnconfirmedAsync(long[] ids) { return await FreeSql.Update() .Set(t => t.Classify == t.UnconfirmedClassify) .Set(t => t.Type == (t.UnconfirmedType ?? t.Type)) .Set(t => t.UnconfirmedClassify, null) .Set(t => t.UnconfirmedType, null) .Where(t => t.UnconfirmedClassify != null && t.UnconfirmedClassify != "") .Where(t => ids.Contains(t.Id)) .ExecuteAffrowsAsync(); } }