28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正

devtools/2025/2/2 9:41:36/

这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能:报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。

一、需求

数据统计方面,我们应该考虑一个问题:用户是否需要看到实时数据。一般来说个人记账软件的数据统计是不需要实时的,因此我们可以将数据统计时间设置为每天统计或者每天每月统计,这样我们不仅可以减少统计数据时受到正在写入的数据的影响,也能提升用户体验。在数据更新方面,我们要在每次新增、删除、更新几张记录时进行更新统计报表。整理后的需求如下:

编号需求说明
1统计支出报表1. 每月、每季度、每年定时统计支出数据
2报表更新1. 新增、删除、更新支出记录时更新报表数据; 2. 如果报表数据不存在则不进行任何处理

二、功能编写

根据前面的需求,我们分别实现这两个功能。

1. 支出数据统计

因为数据每天都定时更新,因此我们要创建一个定时器来实现这个功能,定时器我们依然使用Quartz来实现(以月度报表定时器为例)。我们在Task\Timer文件夹下新建ReportMonthTimer类来实现定时器。代码如下:

using Quartz;
using SporeAccounting.Models;
using SporeAccounting.Server.Interface;namespace SporeAccounting.Task.Timer;/// <summary>
/// 月度报表定时器
/// </summary>
public class ReportMonthTimer : IJob
{private readonly IServiceScopeFactory _serviceScopeFactory;/// <summary>/// 构造函数/// </summary>/// <param name="serviceScopeFactory"></param>public ReportMonthTimer(IServiceScopeFactory serviceScopeFactory){_serviceScopeFactory = serviceScopeFactory;}/// <summary>/// 执行/// </summary>/// <param name="context"></param>/// <returns></returns>public System.Threading.Tasks.Task Execute(IJobExecutionContext context){using var scope = _serviceScopeFactory.CreateScope();// 获取每个用户最近一次报表记录日期var reportServer = scope.ServiceProvider.GetRequiredService<IReportServer>();var incomeExpenditureRecordServer = scope.ServiceProvider.GetRequiredService<IIncomeExpenditureRecordServer>();var reportLogServer = scope.ServiceProvider.GetRequiredService<IReportLogServer>();var reportLogs = reportLogServer.Query();var reportLogDic = reportLogs.GroupBy(x => x.UserId).ToDictionary(x => x.Key,x => x.Max(x => x.CreateDateTime));// 查询上次日期以后的记账记录List<Report> dbReports = new();List<ReportLog> dbReportLogs = new();foreach (var log in reportLogDic){var incomeExpenditureRecords = incomeExpenditureRecordServer.QueryByUserId(log.Key);incomeExpenditureRecords = incomeExpenditureRecords.Where(x => x.RecordDate > log.Value).Where(p => p.IncomeExpenditureClassification.Type == IncomeExpenditureTypeEnmu.Income).ToList();// 生成报表// 按照月度创建报表数据,根据支出类型统计var monthlyReports = incomeExpenditureRecords.GroupBy(x => new { x.RecordDate.Year, x.RecordDate.Month }).Select(g => new Report{Year = g.Key.Year,Month = g.Key.Month,Name = $"{g.Key.Year}{g.Key.Month}月报表",Type = ReportTypeEnum.Month,Amount = g.Sum(x => x.AfterAmount),UserId = log.Key,ClassificationId = g.First().IncomeExpenditureClassificationId,CreateDateTime = DateTime.Now,CreateUserId = log.Key}).ToList();dbReports.AddRange(monthlyReports);// 记录日志var reportLogEntries = dbReports.Select(report => new ReportLog{UserId = report.UserId,ReportId = report.Id,CreateDateTime = DateTime.Now,CreateUserId = report.UserId}).ToList();dbReportLogs.AddRange(reportLogEntries);// 保存报表和日志到数据库reportServer.Add(dbReports);reportLogServer.Add(dbReportLogs);}return System.Threading.Tasks.Task.CompletedTask;}
}

这段代码实现了一个定时任务类ReportMonthTimer,用于生成和记录用户的月度报表。它实现了Quartz库中的IJob接口,定期执行Execute方法。首先,构造函数接收IServiceScopeFactory,用于创建服务作用域,确保每次任务执行时获得正确的服务实例。然后,Execute方法通过依赖注入获取IReportServerIIncomeExpenditureRecordServerIReportLogServer,分别用于处理报表生成、收入支出记录和报表日志。
代码从reportLogServer查询所有报表日志,并根据每个用户的最新报表日期筛选出新的收入支出记录。接着,通过GroupBy按年和月对收入支出记录进行分组,生成月度报表,并将报表数据保存到dbReports中。同时,为每份报表创建日志记录,并保存到dbReportLogs中。最后,报表和日志通过reportServer.Add()reportLogServer.Add()方法存储到数据库。

Tip:这段代码中涉及到了一个新表报表日志,这个用于记录报表数据生成记录的。在这里就不把这个表的结构、操作类列出来了,大家自己动手来实现一下。

2. 报表更新

报表更新逻辑很简单,在这里我们只展示新增的逻辑,其他逻辑大家自己动手实现。我们在IncomeExpenditureRecordImp类的Add方法中添加如下代码:

// 获取包含支出记录记录日期的报表记录
var reports = _sporeAccountingDbContext.Reports.Where(x => x.UserId == incomeExpenditureRecord.UserId&& x.Year <= incomeExpenditureRecord.RecordDate.Year &&x.Month >= incomeExpenditureRecord.RecordDate.Month &&x.ClassificationId==incomeExpenditureRecord.IncomeExpenditureClassificationId);
// 如果没有就说明程序还未将其写入报表,那么就不做任何处理
for (int i = 0; i < reports.Count(); i++)
{var report = reports.ElementAt(i);report.Amount += incomeExpenditureRecord.AfterAmount;_sporeAccountingDbContext.Reports.Update(report);
}

这段代码添加在了if (classification.Type == IncomeExpenditureTypeEnmu.Income) 分支中,当新增的类型时支出项目时,我们就执行这段代码。在这段代码中,当没有查询到支出记录的话就认为对应该日期的指出记录没有进行数据统计,因此不进行任何处理。

三、总结

在这篇文章中,我们介绍了如何在.NET 8环境下实现定时生成财务报表的功能。首先,分析了需求,确定了报表数据统计的时间和更新策略。然后,通过使用Quartz库创建了ReportTimer定时器类,该类实现了IJob接口,并在其Execute方法中实现了报表数据的生成和更新逻辑。在实现过程中,通过依赖注入获取必要的服务实例,查询用户的收入和支出记录,生成季度、年度和月度报表,并将这些报表和日志条目保存到数据库中,实现了报表数据的定期更新和持久化存储。此外,还展示了如何在新增支出记录时更新报表数据,确保报表数据的实时性和准确性。通过这种设计,提高了报表生成的效率,确保了数据的一致性和完整性。希望读者能掌握相关技术并应用到实际项目中。
在下一篇文章,也就是这个专栏的最后一篇文章,我们将一起把这个服务端部署到服务器上。


http://www.ppmy.cn/devtools/155405.html

相关文章

DeepSeek文生图模型Janus-Pro论文解读 —— 多模态AI的革命?

介绍 整个AI行业仍在适应最近发布的、震惊人工智能领域的 DeepSeek-R1。1月28日除夕当天的凌晨&#xff0c;DeepSeek 又发布了另一款出色的开源模型 Janus-Pro。这一次&#xff0c;它是一款能与其他顶级多模态模型相媲美的多模态人工智能模型。 在本文中&#xff0c;我们将解…

openssl 生成证书 windows导入证书

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

【Proteus仿真】【51单片机】多功能计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、加减乘除&#xff0c;开方运算 4、带符号运算 5、最大 999*999 二、使用步骤 基于51单片机多功能计算器 包含&#xff1a;程序&…

Java 中线程的使用

文章目录 Java 线程1 进程2 线程3 线程的基本使用&#xff08;1&#xff09;继承 Thread 类&#xff0c;重写 run 方法&#xff08;2&#xff09;实现 Runnable 接口&#xff0c;重写 run 方法&#xff08;3&#xff09;多线程的使用&#xff08;4&#xff09;线程的理解&#…

Hypium+python鸿蒙原生自动化安装配置

Hypiumpython自动化搭建 文章目录 Python安装pip源配置HDC安装Hypium安装DevEco Testing Hypium插件安装及使用方法​​​​​插件安装工程创建区域 Python安装 推荐从官网获取3.10版本&#xff0c;其他版本可能出现兼容性问题 Python下载地址 下载64/32bitwindows安装文件&am…

C#,入门教程(13)——字符(char)及字符串(string)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(12)——数组及数组使用的基础知识https://blog.csdn.net/beijinghorn/article/details/123918227 字符串的使用与操作是必需掌握得滚瓜烂熟的编程技能之一&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; C#语言实…

Ubuntu下的Doxygen+VScode实现C/C++接口文档自动生成

Ubuntu下的DoxygenVScode实现C/C接口文档自动生成 Chapter1 Ubuntu下的DoxygenVScode实现C/C接口文档自动生成1、 Doxygen简介1. 安装Doxygen1&#xff09;方法一&#xff1a;2&#xff09;方法二&#xff1a;2. doxygen注释自动生成插件3. doxygen注释基本语法4. doxygen的生成…

AI学习指南Ollama篇-使用Ollama构建自己的私有化知识库

一、引言 (一)背景介绍 随着企业对数据隐私和效率的重视,私有化知识库的需求日益增长。私有化知识库不仅可以保护企业数据的安全性,还能提供高效的知识管理和问答系统,提升企业内部的工作效率和创新能力。 (二)Ollama和AnythingLLM的结合 Ollama和AnythingLLM的结合…