feat: Implement scheduled tasks management and budget archiving functionality
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 6s
Docker Build & Deploy / Deploy to Production (push) Has been skipped
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 2s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s

- Added BudgetArchiveJob for monthly budget archiving.
- Created BudgetArchive entity and BudgetArchiveRepository for managing archived budgets.
- Introduced JobController for handling job execution, pausing, and resuming.
- Developed ScheduledTasksView for displaying and managing scheduled tasks in the frontend.
- Updated PeriodicBillJob to improve scope handling.
- Enhanced OpenAiService with increased HTTP timeout.
- Added archiveBudgets API endpoint for archiving budgets by year and month.
- Refactored BudgetController to utilize new repository patterns and improved error handling.
- Introduced rich-content styles for better rendering of HTML content in Vue components.
- Updated various Vue components to support rich HTML content display.
This commit is contained in:
孙诚
2026-01-09 14:03:01 +08:00
parent c5363efc0e
commit ef4ed9fd57
22 changed files with 1129 additions and 459 deletions

View File

@@ -25,7 +25,7 @@ public interface IBaseRepository<T> where T : BaseEntity
/// 添加数据
/// </summary>
Task<bool> AddAsync(T entity);
/// <summary>
/// 添加数据
/// </summary>
@@ -45,6 +45,13 @@ public interface IBaseRepository<T> where T : BaseEntity
/// 删除数据
/// </summary>
Task<bool> DeleteAsync(long id);
/// <summary>
/// 执行动态SQL查询返回动态对象
/// </summary>
/// <param name="completeSql">完整的SELECT SQL语句</param>
/// <returns>动态查询结果列表</returns>
Task<List<dynamic>> ExecuteDynamicSqlAsync(string completeSql);
}
@@ -157,4 +164,22 @@ public abstract class BaseRepository<T>(IFreeSql freeSql) : IBaseRepository<T> w
return false;
}
}
public async Task<List<dynamic>> ExecuteDynamicSqlAsync(string completeSql)
{
var dt = await FreeSql.Ado.ExecuteDataTableAsync(completeSql);
var result = new List<dynamic>();
foreach (System.Data.DataRow row in dt.Rows)
{
var expando = new System.Dynamic.ExpandoObject() as IDictionary<string, object>;
foreach (System.Data.DataColumn column in dt.Columns)
{
expando[column.ColumnName] = row[column];
}
result.Add(expando);
}
return result;
}
}

View File

@@ -0,0 +1,34 @@
namespace Repository;
public interface IBudgetArchiveRepository : IBaseRepository<BudgetArchive>
{
Task<BudgetArchive?> GetArchiveAsync(long budgetId, int year, int month);
Task<List<BudgetArchive>> GetListAsync(int year, int month);
}
public class BudgetArchiveRepository(
IFreeSql freeSql
) : BaseRepository<BudgetArchive>(freeSql), IBudgetArchiveRepository
{
public async Task<BudgetArchive?> GetArchiveAsync(long budgetId, int year, int month)
{
return await FreeSql.Select<BudgetArchive>()
.Where(a => a.BudgetId == budgetId &&
a.Year == year &&
a.Month == month)
.ToOneAsync();
}
public async Task<List<BudgetArchive>> GetListAsync(int year, int month)
{
return await FreeSql.Select<BudgetArchive>()
.Where(
a => a.BudgetType == BudgetPeriodType.Month &&
a.Year == year &&
a.Month == month ||
a.BudgetType == BudgetPeriodType.Year &&
a.Year == year
)
.ToListAsync();
}
}

View File

@@ -146,13 +146,6 @@ public interface ITransactionRecordRepository : IBaseRepository<TransactionRecor
/// <returns>查询结果列表</returns>
Task<List<TransactionRecord>> ExecuteRawSqlAsync(string completeSql);
/// <summary>
/// 执行动态SQL查询返回动态对象
/// </summary>
/// <param name="completeSql">完整的SELECT SQL语句</param>
/// <returns>动态查询结果列表</returns>
Task<List<dynamic>> ExecuteDynamicSqlAsync(string completeSql);
/// <summary>
/// 根据关键词查询已分类的账单(用于智能分类参考)
/// </summary>
@@ -459,23 +452,6 @@ public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository<Tran
return await FreeSql.Ado.QueryAsync<TransactionRecord>(completeSql);
}
public async Task<List<dynamic>> ExecuteDynamicSqlAsync(string completeSql)
{
var dt = await FreeSql.Ado.ExecuteDataTableAsync(completeSql);
var result = new List<dynamic>();
foreach (System.Data.DataRow row in dt.Rows)
{
var expando = new System.Dynamic.ExpandoObject() as IDictionary<string, object>;
foreach (System.Data.DataColumn column in dt.Columns)
{
expando[column.ColumnName] = row[column];
}
result.Add(expando);
}
return result;
}
public async Task<MonthlyStatistics> GetMonthlyStatisticsAsync(int year, int month)
{
var startDate = new DateTime(year, month, 1);