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.pswp.cn/web/67018.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67018.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67018.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于 Node.js 的天氣查詢系統實現(附源碼)

項目概述 這是一個基于 Node.js 的全棧應用,前端使用原生 JavaScript 和 CSS,后端使用 Express 框架,通過調用第三方天氣 API 實現天氣數據的獲取和展示。 主要功能 默認顯示多個主要城市的天氣信息 支持城市天氣搜索 響應式布局設計 深色主題界面 優雅的加載動畫 技術棧 …

cursor重構谷粒商城04——vagrant技術快速部署虛擬機

前言&#xff1a;這個系列將使用最前沿的cursor作為輔助編程工具&#xff0c;來快速開發一些基礎的編程項目。目的是為了在真實項目中&#xff0c;幫助初級程序員快速進階&#xff0c;以最快的速度&#xff0c;效率&#xff0c;快速進階到中高階程序員。 本項目將基于谷粒商城…

leetcode 面試經典 150 題:簡化路徑

鏈接簡化路徑題序號71題型字符串解法棧難度中等熟練度??? 題目 給你一個字符串 path &#xff0c;表示指向某一文件或目錄的 Unix 風格 絕對路徑 &#xff08;以 ‘/’ 開頭&#xff09;&#xff0c;請你將其轉化為 更加簡潔的規范路徑。 在 Unix 風格的文件系統中規則如下…

如何在gitee/github上面搭建obsidian的圖床

在搭建圖床之前我們需要知道圖床是一個什么東西,圖床顧名思義就是存放圖片的地方&#xff0c;那么我們為什么要搭建圖床呢&#xff1f;因為我們在寫博客的時候&#xff0c;很多同學都是在本地使用typora或者是obsidian進行markdown語法的文章的書寫&#xff0c;文件格式通常都是…

JVM堆空間

JVM&#xff08;Java虛擬機&#xff09;堆空間是Java內存管理的核心區域之一&#xff0c;用于存儲Java對象實例。以下是關于JVM堆空間的詳細介紹&#xff1a; 1. 堆空間的作用 ? 存儲對象實例&#xff1a;幾乎所有的Java對象實例&#xff08;通過new關鍵字創建的對象&#xf…

Redis 的熱 Key(Hot Key)問題及解決方法

Redis 的熱 Key&#xff08;Hot Key&#xff09;問題及解決方法 1. 什么是 Redis 熱 Key&#xff1f; Redis 熱 Key&#xff08;Hot Key&#xff09;指的是訪問頻率極高的 Key&#xff0c;通常會造成以下問題&#xff1a; 單 Key 訪問量過大&#xff1a;熱點 Key 可能被高并…

SSM東理咨詢交流論壇

&#x1f345;點贊收藏關注 → 添加文檔最下方聯系方式咨詢本源代碼、數據庫&#x1f345; 本人在Java畢業設計領域有多年的經驗&#xff0c;陸續會更新更多優質的Java實戰項目希望你能有所收獲&#xff0c;少走一些彎路。&#x1f345;關注我不迷路&#x1f345; 項目視頻 js…

http的請求體各項解析

一、前言 做Java開發的人員都知道&#xff0c;其實我們很多時候不單單在寫Java程序。做的各種各樣的系統&#xff0c;不管是PC的 還是移動端的&#xff0c;還是為別的系統提供接口。其實都離不開http協議或者https 這些東西。Java作為編程語言&#xff0c;再做業務開發時&#…

gradle生命周期鉤子函數

文章目錄 0. 總結表格1. 構建初始階段gradle.settingsEvaluated()gradle.projectsLoaded() 2. 配置階段gradle.beforeProject()gradle.afterProject()gradle.projectEvaluated()gradle.afterEvaluate()gradle.taskGraph.whenReady 3. 執行階段gradle.taskGraph.beforeTaskgradl…

Qt Enter和HoverEnter事件

介紹 做PC開發的過程中或多或少都會接觸到鼠標的懸停事件&#xff0c;Qt中處理鼠標懸停有Enter和HoverEnter兩種事件 相同點 QEvent::Enter對應QEnterEvent&#xff0c;描述的是鼠標進入控件坐標范圍之內的行為&#xff0c;QEnterEvent可以抓取鼠標的位置&#xff1b;QEvent…

【云安全】云原生-Docker(五)容器逃逸之漏洞利用

漏洞利用逃逸 通過漏洞利用實現逃逸&#xff0c;主要分為以下兩種方式&#xff1a; 1、操作系統層面的內核漏洞 這是利用宿主機操作系統內核中的安全漏洞&#xff0c;直接突破容器的隔離機制&#xff0c;獲得宿主機的權限。 攻擊原理&#xff1a;容器本質上是通過 Linux 的…

如何優化深度學習模型來提高錯別字檢測準確率?

為了優化深度學習模型以提高錯別字檢測的準確率,可以從以下幾個方面入手: 1. 數據增強 數據增強是提高模型泛化能力的有效方法。通過在訓練數據中引入噪聲,模型可以學習到更多變的模式,從而提高對未見數據的識別能力。 刪除字符:以一定概率刪除文本中的一個字符。增加字…

二叉搜索樹中的搜索(力扣700)

首先介紹一下什么是二叉搜索樹。 二叉搜索樹是一個有序樹&#xff1a; 若它的左子樹不空&#xff0c;則左子樹上所有結點的值均小于它的根結點的值&#xff1b;若它的右子樹不空&#xff0c;則右子樹上所有結點的值均大于它的根結點的值&#xff1b;它的左、右子樹也分別為二叉…

pytest自動化測試 - 構造“預置條件”的幾種方式

<< 返回目錄 1 pytest自動化測試 - 構造“預置條件”的幾種方式 1.1 使用夾具構造預置條件 在夾具章節中&#xff0c;我們介紹了夾具的作用&#xff0c;其中一項就是構造預置條件。pytest.fixture裝飾器中如果測試數據使用yield返回&#xff0c;則yield前的語句為預置條…

微信小程序date picker的一些說明

微信小程序的picker是一個功能強大的組件&#xff0c;它可以是一個普通選擇器&#xff0c;也可以是多項選擇器&#xff0c;也可以是時間、日期、省市區選擇器。 官方文檔在這里 這里講一下date picker的用法。 <view class"section"><view class"se…

[java] 面向對象進階篇1--黑馬程序員

目錄 static 靜態變量及其訪問 實例變量及其訪問 靜態方法及其訪問 實例方法及其訪問 總結 繼承 作用 定義格式 示例 總結 子類不能繼承的內容 繼承后的特點 成員變量 成員變量不重名 成員變量重名 super訪問父類成員變量 成員方法 成員方法不重名 成員方法…

python3+TensorFlow 2.x 基礎學習(一)

目錄 TensorFlow 2.x基礎 1、安裝 TensorFlow 2.x 2、TensorFlow 2.x 基礎概念 2、1 Eager Execution 2、2 TensorFlow 張量&#xff08;Tensor&#xff09; 3、使用Keras構建神經網絡模型 3、1 構建 Sequential 模型 3、2 編譯模型 1、Optimizer&#xff08;優化器&a…

AI News(1/21/2025):OpenAI 安全疏忽:ChatGPT漏洞引發DDoS風險/OpenAI 代理工具即將發布

1、OpenAI 的安全疏忽&#xff1a;ChatGPT API 漏洞引發DDoS風險 德國安全研究員 Benjamin Flesch 發現了一個嚴重的安全漏洞&#xff1a;攻擊者可以通過向 ChatGPT API 發送一個 HTTP 請求&#xff0c;利用 ChatGPT 的爬蟲對目標網站發起 DDoS 攻擊。該漏洞源于 OpenAI 在處理…

openlava/LSF 用戶組管理腳本

背景 在openlava運維中經常需要自動化一些常規操作&#xff0c;比如增加用戶組以及組成員、刪除用戶組成員、刪除用戶組等。而openlava的配置文件需要手動修改&#xff0c;然后再通過badmin reconfig激活配置。因此開發腳本將手工操作自動化就很有必要。 通過將腳本中的User…

LLMs的星辰大海:大語言模型的前世今生

文章目錄 一. LLM 的演進&#xff1a;從規則到智能的躍遷 &#x1f4ab;1.1 語言模型的蹣跚起步 &#x1f476;1.2 RNN 與 LSTM&#xff1a;序列建模的嘗試 &#x1f9d0;1.3 Transformer 的橫空出世&#xff1a;自注意力機制的革命 &#x1f4a5;1.4 LLM &#xff1a;從預測到…