27. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表服务
报表是每个记账应用所具备的功能,要实现报表功能就需要把账本的核心功能(记账)完成,因此报表服务作为本专栏第一部分单体应用开发中最后一个要实现的功能,这一篇文章很简单,我们一起来实现一个简单的报表服务。
一、需求
需求很简单,我们只需要提供一个接口供客户端查询时使用,下面是需求。
| 编号 | 需求 | 说明 |
|---|---|---|
| 1 | 报表查询 | 1. 传入报表类型,年份、月份查询对应的报表数据;2. 月份参数可以为空;3. 报表类型包括:月报表、季度报表、年报表 |
二、功能编写
-
模型编写
根据上一小节的需求,我们得出了传入的参数需要包含:报表类型、年份以及月份,其中月份可以为空,视图模型如下:
using System.ComponentModel.DataAnnotations;namespace SporeAccounting.Models.ViewModels;/// <summary> /// 报表视图模型 /// </summary> public class ReportViewModel {/// <summary>/// 报表类型/// </summary>[Required(ErrorMessage = "报表类型不能为空")]public ReportTypeEnum ReportType { get; set; }/// <summary>/// 年份/// </summary>[Required(ErrorMessage = "年份不能为空")]public int Year { get; set; }/// <summary>/// 月份/// </summary>public int? Month { get; set; } }我们一起来看看数据库表映射类需要包含哪些属性吧。首先,需要年份、月份和季度这几个字段,它们可以提供清晰的时间维度,方便用户在不同时间范围内查询和统计数据,比如生成年度报告、月度账单或季度分析等。
其次,需要一个主题或用途字段,用于标识报表的具体内容。此外,还需要一个报表类型字段,可以用枚举值来区分不同类型的报表(如季度报表、月度报表、年度报表)。
因为报表通常涉及金额,所以必须有一个金额字段来准确记录相关数据。为了帮助用户清楚了解每个支出分类的具体占比和总金额,还需要一个支出分类字段。
最后,考虑到每个报表只能对应一个用户,我们还需要一个字段来标识用户,以确保报表和用户之间的关联关系。
这样设计,既能满足报表统计和分析的需求,也能保证数据结构清晰合理。通过分析,我们得出如下代码:using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using SporeAccounting.BaseModels;namespace SporeAccounting.Models;/// <summary> /// 报表 /// </summary> [Table("Report")] public class Report : BaseModel {/// <summary>/// 年份/// </summary>[Required][Column(TypeName = "int")]public int Year { get; set; }/// <summary>/// 月份/// </summary>[Column(TypeName = "int")]public int? Month { get; set; }/// <summary>/// 季度/// </summary>[Column(TypeName = "int")]public int? Quarter { get; set; }/// <summary>/// 报表名称/// </summary>[Required][Column(TypeName = "nvarchar(100)")]public string Name { get; set; }/// <summary>/// 报表类型/// </summary>[Required][Column(TypeName = "int")]public ReportTypeEnum Type { get; set; }/// <summary>/// 金额/// </summary>[Required][Column(TypeName = "decimal(18,2)")]public decimal Amount { get; set; }/// <summary>/// 用户Id/// </summary>[Required][Column(TypeName = "nvarchar(36)")][ForeignKey("FK_Report_SysUser_UserId")]public string UserId { get; set; }/// <summary>/// 分类Id/// </summary>[Required][Column(TypeName = "nvarchar(36)")][ForeignKey("FK_Report_Classification_ClassificationId")]public string ClassificationId { get; set; }/// <summary>/// 导航属性/// </summary>public SysUser User { get; set; }/// <summary>/// 导航属性/// </summary>public IncomeExpenditureClassification Classification { get; set; } } -
功能实现
我们这里只展示和查询报表有关的功能代码,其他代码不再展示。首先,我们要定义数据服务接口IReportServer,在这个接口中添加获取报表的方法。using SporeAccounting.Models;namespace SporeAccounting.Server.Interface;/// <summary> /// 报表服务 /// </summary> public interface IReportServer { /// <summary>/// 获取报表数据/// </summary>/// <param name="userId"></param>/// <param name="year"></param>/// <param name="reportType"></param>/// <returns></returns>List<Report> QueryReport(string userId, int year,ReportTypeEnum reportType); }然后,我们实现这个接口,实现也很简单很简单,我就不具体讲解了。
using SporeAccounting.Models; using SporeAccounting.Server.Interface;namespace SporeAccounting.Server;/// <summary> /// 报表服务实现 /// </summary> public class ReportImp : IReportServer {private readonly SporeAccountingDBContext _sporeAccountingDbContext;public ReportImp(SporeAccountingDBContext sporeAccountingDbContext){_sporeAccountingDbContext = sporeAccountingDbContext;}/// <summary>/// 获取报表数据/// </summary>/// <param name="userId"></param>/// <param name="year"></param>/// <returns></returns>public List<Report> QueryReport(string userId, int year, ReportTypeEnum reportType){try{IQueryable<Report> reports = _sporeAccountingDbContext.Reports.Where(p => p.UserId == userId && p.Year == year && p.Type == reportType);return reports.ToList();}catch (Exception e){throw;}} }最后,我们一起编写Controller。
using System.Net; using AutoMapper; using Microsoft.AspNetCore.Mvc; using SporeAccounting.BaseModels; using SporeAccounting.Models.ViewModels; using SporeAccounting.Server.Interface;namespace SporeAccounting.Controllers {/// <summary>/// 报表控制器/// </summary>[Route("api/[controller]")][ApiController]public class ReportController : BaseController{/// <summary>/// 报表服务/// </summary>private IReportServer _reportServer;/// <summary>/// 映射/// </summary>private IMapper _mapper;/// <summary>/// 构造函数/// </summary>/// <param name="reportServer"></param>/// <param name="mapper"></param>public ReportController(IReportServer reportServer, IMapper mapper){_reportServer = reportServer;_mapper = mapper;}/// <summary>/// 获取报表/// </summary>/// <param name="report"></param>/// <returns></returns>[HttpPost][Route("GetReport")]public ActionResult<ResponseData<List<ReportResponseViewModel>>> GetReport([FromBody] ReportViewModel report){try{string userId = GetUserId();var reports = _reportServer.QueryReport(userId, report.Year, report.ReportType);List<ReportResponseViewModel> response = _mapper.Map<List<ReportResponseViewModel>>(reports);return Ok(new ResponseData<List<ReportResponseViewModel>>(HttpStatusCode.OK, data: response));}catch (Exception ex){return Ok(new ResponseData<bool>(HttpStatusCode.BadRequest, errorMessage: ex.Message));}}} }
三、总结
本文介绍了记账应用中的报表功能实现,作为单体应用开发的最后环节,其核心是提供一个接口供客户端查询报表数据。本功能实现以清晰的需求分析为起点,逐步完成了模型设计、服务接口定义、接口实现以及控制器编写。需求方面,报表查询需支持按报表类型(包括月报、季报、年报)和时间维度(年份、月份)进行查询,其中月份参数为可选。功能设计中,视图模型定义了报表类型、年份和月份的基本字段,并通过校验属性确保必填字段的正确性。数据库模型进一步扩展了字段设计,涵盖了年份、月份、季度、报表名称、类型、金额、用户关联等内容,满足多维度数据统计和查询需求。服务层通过IReportServer接口和实现类ReportImp完成核心数据查询逻辑,利用数据库上下文筛选符合条件的报表数据。最后,控制器ReportController提供了一个GetReport接口,负责接收客户端请求并返回查询结果。通过依赖注入服务和自动映射工具,接口实现高效、简洁。整体设计从需求到实现逻辑清晰,功能模块化,便于后续扩展和维护。
在下一篇文章中我们将一起编写报表定时器,实现定时汇总支出数据功能。
相关文章:
27. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表服务
报表是每个记账应用所具备的功能,要实现报表功能就需要把账本的核心功能(记账)完成,因此报表服务作为本专栏第一部分单体应用开发中最后一个要实现的功能,这一篇文章很简单,我们一起来实现一个简单的报表服…...
coffee销售数据集分析:基于时间趋势分析的实操练习
**文章说明:**对coffee销售数据集的简单分析练习(时间趋势分析练习),主要是为了强化利用python进行数据分析的实操能力。属于个人的练习文章。 **注:**这是我第一次使用md格式编辑博客文章,排版上还是不是很…...
【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能
【1】原贴地址:eclipse - 怎么还原原来版本的搜索功能_eclipse打开类型搜索类功能失效-CSDN博客 https://blog.csdn.net/sinat_32238399/article/details/145113105 【2】原文如下: 更新eclipse-24-09版本后之后,新的搜索功能(CT…...
Centos 修改历史读录( HISTSIZE)
history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件,默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…...
lwIP——4 网络接口
1.lwIP网络接口 网络接口(网卡):个人理解是处理网络层和数据传输关系的接口(tcp/ip协议栈中的网络接口层部分),直接与硬件平台打交道 lwIP协议栈支持多种不同的网络接口(网卡)&#…...
pytest自动化测试 - pytest夹具的基本概念
<< 返回目录 1 pytest自动化测试 - pytest夹具的基本概念 夹具可以为测试用例提供资源(测试数据)、执行预置条件、执行后置条件,夹具可以是函数、类或模块,使用pytest.fixture装饰器进行标记。 1.1 夹具的作用范围 夹具的作用范围: …...
FreeRtos的使用教程
定义: RTOS实时操作系统, (Real Time Operating System), 指的是当外界事件发生时, 能够有够快的响应速度,调度一切可利用的资源, 控制实时任务协调一致的运行。 特点: 支持多任务管理, 处理多个事件, 实现更复杂的逻辑。 与计算…...
yolov11 解读简记
1 文章详细介绍了YOLOv11的架构设计,包括以下几个关键组件: C3k2块:这是YOLOv11引入的一种新型卷积块,替代了之前版本中的C2f块。C3k2块通过使用两个较小的卷积核代替一个大的卷积核,提高了计算效率,同时保…...
实验二 数据库的附加/分离、导入/导出与备份/还原
实验二 数据库的附加/分离、导入/导出与备份/还原 一、实验目的 1、理解备份的基本概念,掌握各种备份数据库的方法。 2、掌握如何从备份中还原数据库。 3、掌握数据库中各种数据的导入/导出。 4、掌握数据库的附加与分离,理解数据库的附加与分离的作用。…...
Kafka常见问题之 `javax.management.InstanceAlreadyExistsException`
文章目录 Kafka常见问题之 javax.management.InstanceAlreadyExistsException1. 概述2. 常见原因3. 具体异常示例4. 解决方案4.1 确保单一 Kafka Producer 实例4.2 配置 Kafka Broker 和 Producer 使用唯一的 JMX 名称(对于Producer重点检查 client.id)4…...
性能测试丨JVM 性能数据采集
什么是JVM性能数据采集? JVM性能数据采集是指通过一些工具和技术采集与Java虚拟机相关的性能数据。这些数据包括但不限于内存使用、CPU使用、垃圾回收(GC)行为、线程活动等。合理地分析这些数据,可以帮助我们找出系统的瓶颈&…...
计算机图形学实验练习(实验1.2-4.1AND补充实验12)
实验1.2 OpenGL与着色器编程 1.理论知识 1.1 OpenGL的含义 OpenGL是一种应用程序编程接口(Application Programming Interface,API),它是一种可以对图形硬件设备特性进行访问的软件库。OpenGL最新的4.3版本包含了超过500个不同的命令,可以用于设置所需的对象、图像和操…...
JWT实现单点登录
文章目录 JWT实现单点登录JWT 简介存在问题及解决方案登录流程后端程序实现前端保存Tokenstore存放信息的缺点及解决 校验流程:为gateway增加登录校验拦截器 另一种单点登录方法:Token+Redis实现单点登录 JWT实现单点登录 登录流程ÿ…...
云计算的概念与特点:开启数字化时代的新篇章
在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…...
salesforce中如何获取一个profile的18位id
在 Salesforce 中,要获取一个 Profile 的 18 位 ID,可以通过以下几种方式实现: 方法 1:通过 Developer Console 登录 Salesforce。 点击右上角的 头像 或 设置齿轮,选择 “开发者控制台”(Developer Conso…...
Vue 3 中的标签 ref 与 defineExpose:模板引用与组件暴露
在 Vue 3 中,ref 不仅可以用于创建响应式数据,还可以用于获取 DOM 节点或组件实例。通过 ref,我们可以直接访问模板中的元素或组件,并在需要时操作它们。此外,defineExpose 用于在 <script setup> 语法中显式暴露…...
FLTK - FLTK1.4.1 - demo - adjuster.exe
文章目录 FLTK - FLTK1.4.1 - demo - adjuster.exe概述笔记根据代码,用fluid重建一个adjuster.fl 备注 - fluid生成的代码作为参考代码好了修改后可用的代码END FLTK - FLTK1.4.1 - demo - adjuster.exe 概述 想过一遍 FLTK1.4.1的demo和测试工程,工程…...
单路由及双路由端口映射指南
远程登录总会遇到登陆不上的情况,可能是访问的大门没有打开哦,下面我们来看看具体是怎么回事? 当软件远程访问时,主机需要两个条件,一是有一个唯一的公网IP地址(运营商提供),二是开…...
专为课堂打造:宏碁推出三款全新耐用型 Chromebook
IT之家 1 月 25 日消息,宏碁(Acer)昨日(1 月 24 日)发布公告,针对教育市场,推出 Chromebook Spin 512 (R857T)、Chromebook Spin 511 (R757T) 和 Chromebook 511 (C737) 三款产品,兼…...
云计算架构学习之LNMP架构部署、架构拆分、负载均衡-会话保持
一.LNMP架构部署 1.1. LNMP服务搭建 1.磁盘信息 2.内存 3.负载信息 4.Nginx你们公司都用来干嘛 5.文件句柄(文件描述符 打开文件最大数量) 6.你处理过系统中的漏洞吗 SSH漏洞 7.你写过什么shell脚本 8.监控通过什么告警 zabbix 具体监控哪些内容 9.mysql redis查询 你好H…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
