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

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接口,负责接收客户端请求并返回查询结果。通过依赖注入服务和自动映射工具,接口实现高效、简洁。整体设计从需求到实现逻辑清晰,功能模块化,便于后续扩展和维护。
在下一篇文章中我们将一起编写报表定时器,实现定时汇总支出数据功能。


http://www.kler.cn/a/519602.html

相关文章:

  • idea实用设置
  • 1.23 补题 寒假训练营
  • JAVA:利用 Content Negotiation 实现多样式响应格式的技术指南
  • 2025_1_26 c++中关于构造和析构的顺序
  • 飞牛 fnOS 安装8852be网卡驱动并成功连接
  • cuda reductionreduce
  • Docker 系列之 docker-compose 容器编排详解
  • 【信息系统项目管理师-选择真题】2017上半年综合知识答案和详解
  • Transfoemr的解码器(Decoder)与分词技术
  • QT:控件属性及常用控件(4)-----多元素控件、容器类控件、布局管理器
  • 3.numpy练习(2)
  • RabbitMQ 分布式高可用
  • 【Linux】Linux编译器-g++、gcc、动静态库
  • 7、知识库内容更新与自动化
  • 系统编程(线程互斥)
  • 牛角棋项目实践1:牛角棋的定义和用python实现简单功能
  • 大模型开发 | RAG在实际开发中可能遇到的坑
  • rewrite规则
  • STL中的list容器
  • 汇编的使用总结
  • CSS:跑马灯
  • 使用MQTT.fx向阿里云物理网平台上报物理模型数据
  • mysql安装记录
  • 十、VUE中的CSS
  • 边缘存储如何高效存储和调用数据?
  • “AI教学实训系统:打造未来教育的超级引擎