当前位置: 首页 > news >正文

27. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表服务

报表是每个记账应用所具备的功能,要实现报表功能就需要把账本的核心功能(记账)完成,因此报表服务作为本专栏第一部分单体应用开发中最后一个要实现的功能,这一篇文章很简单,我们一起来实现一个简单的报表服务。

一、需求

需求很简单,我们只需要提供一个接口供客户端查询时使用,下面是需求。

编号需求说明
1报表查询1. 传入报表类型,年份、月份查询对应的报表数据;2. 月份参数可以为空;3. 报表类型包括:月报表、季度报表、年报表

二、功能编写

  1. 模型编写

    根据上一小节的需求,我们得出了传入的参数需要包含:报表类型、年份以及月份,其中月份可以为空,视图模型如下:

    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; }
    }
    
  2. 功能实现
    我们这里只展示和查询报表有关的功能代码,其他代码不再展示。首先,我们要定义数据服务接口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 实战--孢子记账--从单体到微服务】--简易报表--报表服务

报表是每个记账应用所具备的功能&#xff0c;要实现报表功能就需要把账本的核心功能&#xff08;记账&#xff09;完成&#xff0c;因此报表服务作为本专栏第一部分单体应用开发中最后一个要实现的功能&#xff0c;这一篇文章很简单&#xff0c;我们一起来实现一个简单的报表服…...

coffee销售数据集分析:基于时间趋势分析的实操练习

**文章说明&#xff1a;**对coffee销售数据集的简单分析练习&#xff08;时间趋势分析练习&#xff09;&#xff0c;主要是为了强化利用python进行数据分析的实操能力。属于个人的练习文章。 **注&#xff1a;**这是我第一次使用md格式编辑博客文章&#xff0c;排版上还是不是很…...

【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能

【1】原贴地址&#xff1a;eclipse - 怎么还原原来版本的搜索功能_eclipse打开类型搜索类功能失效-CSDN博客 https://blog.csdn.net/sinat_32238399/article/details/145113105 【2】原文如下&#xff1a; 更新eclipse-24-09版本后之后&#xff0c;新的搜索功能&#xff08;CT…...

Centos 修改历史读录( HISTSIZE)

history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件&#xff0c;默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…...

lwIP——4 网络接口

1.lwIP网络接口 网络接口&#xff08;网卡&#xff09;&#xff1a;个人理解是处理网络层和数据传输关系的接口&#xff08;tcp/ip协议栈中的网络接口层部分&#xff09;&#xff0c;直接与硬件平台打交道 lwIP协议栈支持多种不同的网络接口&#xff08;网卡&#xff09;&#…...

pytest自动化测试 - pytest夹具的基本概念

<< 返回目录 1 pytest自动化测试 - pytest夹具的基本概念 夹具可以为测试用例提供资源(测试数据)、执行预置条件、执行后置条件&#xff0c;夹具可以是函数、类或模块&#xff0c;使用pytest.fixture装饰器进行标记。 1.1 夹具的作用范围 夹具的作用范围&#xff1a; …...

FreeRtos的使用教程

定义&#xff1a; RTOS实时操作系统, (Real Time Operating System), 指的是当外界事件发生时, 能够有够快的响应速度,调度一切可利用的资源, 控制实时任务协调一致的运行。 特点&#xff1a; 支持多任务管理&#xff0c; 处理多个事件&#xff0c; 实现更复杂的逻辑。 与计算…...

yolov11 解读简记

1 文章详细介绍了YOLOv11的架构设计&#xff0c;包括以下几个关键组件&#xff1a; C3k2块&#xff1a;这是YOLOv11引入的一种新型卷积块&#xff0c;替代了之前版本中的C2f块。C3k2块通过使用两个较小的卷积核代替一个大的卷积核&#xff0c;提高了计算效率&#xff0c;同时保…...

实验二 数据库的附加/分离、导入/导出与备份/还原

实验二 数据库的附加/分离、导入/导出与备份/还原 一、实验目的 1、理解备份的基本概念&#xff0c;掌握各种备份数据库的方法。 2、掌握如何从备份中还原数据库。 3、掌握数据库中各种数据的导入/导出。 4、掌握数据库的附加与分离&#xff0c;理解数据库的附加与分离的作用。…...

Kafka常见问题之 `javax.management.InstanceAlreadyExistsException`

文章目录 Kafka常见问题之 javax.management.InstanceAlreadyExistsException1. 概述2. 常见原因3. 具体异常示例4. 解决方案4.1 确保单一 Kafka Producer 实例4.2 配置 Kafka Broker 和 Producer 使用唯一的 JMX 名称&#xff08;对于Producer重点检查 client.id&#xff09;4…...

性能测试丨JVM 性能数据采集

什么是JVM性能数据采集&#xff1f; JVM性能数据采集是指通过一些工具和技术采集与Java虚拟机相关的性能数据。这些数据包括但不限于内存使用、CPU使用、垃圾回收&#xff08;GC&#xff09;行为、线程活动等。合理地分析这些数据&#xff0c;可以帮助我们找出系统的瓶颈&…...

计算机图形学实验练习(实验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存放信息的缺点及解决 校验流程&#xff1a;为gateway增加登录校验拦截器 另一种单点登录方法&#xff1a;Token&#xff0b;Redis实现单点登录 JWT实现单点登录 登录流程&#xff…...

云计算的概念与特点:开启数字化时代的新篇章

在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…...

salesforce中如何获取一个profile的18位id

在 Salesforce 中&#xff0c;要获取一个 Profile 的 18 位 ID&#xff0c;可以通过以下几种方式实现&#xff1a; 方法 1&#xff1a;通过 Developer Console 登录 Salesforce。 点击右上角的 头像 或 设置齿轮&#xff0c;选择 “开发者控制台”&#xff08;Developer Conso…...

Vue 3 中的标签 ref 与 defineExpose:模板引用与组件暴露

在 Vue 3 中&#xff0c;ref 不仅可以用于创建响应式数据&#xff0c;还可以用于获取 DOM 节点或组件实例。通过 ref&#xff0c;我们可以直接访问模板中的元素或组件&#xff0c;并在需要时操作它们。此外&#xff0c;defineExpose 用于在 <script setup> 语法中显式暴露…...

FLTK - FLTK1.4.1 - demo - adjuster.exe

文章目录 FLTK - FLTK1.4.1 - demo - adjuster.exe概述笔记根据代码&#xff0c;用fluid重建一个adjuster.fl 备注 - fluid生成的代码作为参考代码好了修改后可用的代码END FLTK - FLTK1.4.1 - demo - adjuster.exe 概述 想过一遍 FLTK1.4.1的demo和测试工程&#xff0c;工程…...

单路由及双路由端口映射指南

远程登录总会遇到登陆不上的情况&#xff0c;可能是访问的大门没有打开哦&#xff0c;下面我们来看看具体是怎么回事&#xff1f; 当软件远程访问时&#xff0c;主机需要两个条件&#xff0c;一是有一个唯一的公网IP地址&#xff08;运营商提供&#xff09;&#xff0c;二是开…...

专为课堂打造:宏碁推出三款全新耐用型 Chromebook

IT之家 1 月 25 日消息&#xff0c;宏碁&#xff08;Acer&#xff09;昨日&#xff08;1 月 24 日&#xff09;发布公告&#xff0c;针对教育市场&#xff0c;推出 Chromebook Spin 512 (R857T)、Chromebook Spin 511 (R757T) 和 Chromebook 511 (C737) 三款产品&#xff0c;兼…...

云计算架构学习之LNMP架构部署、架构拆分、负载均衡-会话保持

一.LNMP架构部署 1.1. LNMP服务搭建 1.磁盘信息 2.内存 3.负载信息 4.Nginx你们公司都用来干嘛 5.文件句柄(文件描述符 打开文件最大数量) 6.你处理过系统中的漏洞吗 SSH漏洞 7.你写过什么shell脚本 8.监控通过什么告警 zabbix 具体监控哪些内容 9.mysql redis查询 你好H…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...