23. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--预算

在每个月发工资后很多人会对未来一个月的花销进行大致的计划,这个行为叫做预算。那么在这篇文章中我们将一起开发预算服务。

一、需求

预算需求就是简单的增删改查,虽然比较简单,但是也有几点需要注意。

编号需求说明
1新增预算1. 针对每种支出类型设置预算;2. 每个用户每种支出类型只能有一条预算
2删除预算
3修改预算1. 不能修改预算的支出类型
4查询预算
5预算周期设置1. 用户可设置预算的周期,按照年、季度、月设置;2. 设置预算的适用范围

根据上面的分析,我们可以得出预算表Budget的核心字段:支出类型的Id、预算金额、预算周期、预算开始时间、预算结束时间。这里要着重说一下为什么有了预算的周期还要有预算的开始时间和结束时间。这是因为用户在设置预算的时候有可能设置的是未来某个时间段内的预算。

二、功能编写

下面我们以新增预算为例,来看一下如何实现新增预算的功能呢。

2.1 编写数据库映射类

根据前面分析的结果,我们编写出了数据库预算表的映射类Budget

using System.ComponentModel.DataAnnotations;
using SporeAccounting.BaseModels;
using System.ComponentModel.DataAnnotations.Schema;namespace SporeAccounting.Models;/// <summary>
/// 预算表
/// </summary>
[Table(name: "Budget")]
public class Budget : BaseModel
{/// <summary>/// 收支类型/// </summary>[Required][Column(TypeName = "nvarchar(36)")]public string IncomeExpenditureClassificationId { get; set; }/// <summary>/// 预算金额/// </summary>[Required][Column(TypeName = "decimal(18,2)")]public decimal Amount { get; set; }/// <summary>/// 预算周期/// </summary>[Column(TypeName = "int")][Required]public PeriodEnum Period { get; set; }/// <summary>/// 剩余预算/// </summary>[Required][Column(TypeName = "decimal(18,2)")]public decimal Remaining { get; set; }/// <summary>/// 备注/// </summary>[MaxLength(200)]public string? Remark { get; set; }/// <summary>/// 开始时间/// </summary>[Required][Column(TypeName = "datetime")]public DateTime StartTime { get; set; }/// <summary>/// 结束时间/// </summary>[Required][Column(TypeName = "datetime")]public DateTime EndTime { get; set; }/// <summary>/// 用户Id/// </summary>[Required][Column(TypeName = "nvarchar(36)")][ForeignKey("FK_Budget_SysUser")]public string UserId { get; set; }/// <summary>/// 导航属性/// </summary>public SysUser SysUser { get; set; }/// <summary>/// 导航属性/// </summary>public IncomeExpenditureClassification Classification { get; set; }= new IncomeExpenditureClassification();
}

Budget 类我就不做过多的讲解了,大家在编写玩Budget 类后一定要记得将这个类添加到数据库连接上下文类SporeAccountingDBContext中,然后执行数据库迁移命令。

2.2 编写Server服务

我们在Server文件夹下的Interface文件夹中新建预算Server接口IBudgetServer,在这个接口中增加新增预算的方法Add,以及判断当前用户是否存在指定支出类型预算的方法IsExistByClassificationId,代码如下“

using SporeAccounting.Models;namespace SporeAccounting.Server.Interface;/// <summary>
/// 预算服务
/// </summary>
public interface IBudgetServer
{/// <summary>/// 添加预算/// </summary>/// <param name="budget"></param>void Add(Budget budget);/// <summary>/// 用户是否存在该类型预算/// </summary>/// <param name="classificationId"></param>/// <param name="userId"></param>/// <returns></returns>bool IsExistByClassificationId(string classificationId, string userId);
}

接着,我们实现IBudgetServer接口,在Server文件夹下创建实现类BudgetImp,代码如下:

using SporeAccounting.Models;
using SporeAccounting.Server.Interface;namespace SporeAccounting.Server;/// <summary>
/// 预算服务
/// </summary>
public class BudgetImp : IBudgetServer
{/// <summary>/// 数据库上下文/// </summary>private readonly SporeAccountingDBContext _sporeAccountingDbContext;/// <summary>/// 构造函数/// </summary>/// <param name="sporeAccountingDbContext"></param>public BudgetImp(SporeAccountingDBContext sporeAccountingDbContext){_sporeAccountingDbContext = sporeAccountingDbContext;}/// <summary>/// 添加预算/// </summary>/// <param name="budget"></param>public void Add(Budget budget){try{_sporeAccountingDbContext.Budgets.Add(budget);_sporeAccountingDbContext.SaveChanges();}catch (Exception e){throw;}}/// <summary>/// 用户是否存在该类型预算/// </summary>/// <param name="classificationId"></param>/// <param name="userId"></param>/// <returns></returns>public bool IsExistByClassificationId(string classificationId, string userId){try{return _sporeAccountingDbContext.Budgets.Any(b =>b.IncomeExpenditureClassificationId == classificationId && b.UserId == userId);}catch (Exception e){throw;}}
}

类中的Add方法用于将一个Budget对象添加到数据库中。该方法调用了数据库上下文的Budgets.Add方法将预算对象添加到数据库的追踪列表中,然后通过SaveChanges方法将更改保存到数据库中。IsExistByClassificationId方法的作用是判断某个用户是否已经存在特定分类的预算记录。它接受两个参数:预算分类的IDclassificationId和用户IDuserId,通过_sporeAccountingDbContext.Budgets.Any方法执行数据库查询,返回布尔值。
Server编写完成后别忘了将Budget服务注入到我们的项目中。

2.3 编写新增预算服务接口

最后,我们来编写新增预算的服务接口。我们需要先定义新增预算的视图模型,这个视图模型不需要预算Id,其他的和Budget类一样。

using System.ComponentModel.DataAnnotations;namespace SporeAccounting.Models.ViewModels;/// <summary>
/// 预算添加视图模型
/// </summary>
public class BudgetAddViewModel
{/// <summary>/// 预算金额/// </summary>[Required(ErrorMessage = "预算金额不能为空")]public decimal Amount { get; set; }/// <summary>/// 周期/// </summary>[Required(ErrorMessage = "周期不能为空")]public PeriodEnum Period { get; set; }/// <summary>/// 开始时间/// </summary>[Required(ErrorMessage = "开始时间不能为空")]public DateTime StartTime { get; set; }/// <summary>/// 结束时间/// </summary>[Required(ErrorMessage = "结束时间不能为空")]public DateTime EndTime { get; set; }/// <summary>/// 收支分类/// </summary>[Required(ErrorMessage = "收支分类不能为空")]public string ClassificationId { get; set; }/// <summary>/// 备注/// </summary>[MaxLength(200)]public string? Remark { get; set; }
}

接着,我们新建BudgetController,并在增加Add Action。代码如下:

using System.Net;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SporeAccounting.BaseModels;
using SporeAccounting.Models;
using SporeAccounting.Models.ViewModels;
using SporeAccounting.Server.Interface;namespace SporeAccounting.Controllers
{/// <summary>/// 预算控制器/// </summary>[Route("api/[controller]")][ApiController]public class BudgetController : BaseController{/// <summary>/// 预算服务/// </summary>private IBudgetServer _budgetServer;private IMapper _mapper;/// <summary>/// 构造函数/// </summary>/// <param name="budgetServer"></param>/// <param name="mapper"></param>public BudgetController(IBudgetServer budgetServer, IMapper mapper){_budgetServer = budgetServer;_mapper = mapper;}/// <summary>/// 添加预算/// </summary>/// <param name="budget"></param>/// <returns></returns>[HttpPost][Route("Add")]public ActionResult<ResponseData<bool>> Add([FromBody] BudgetAddViewModel budget){try{string userId = GetUserId();// 用户是否存在该类型预算bool isExist = _budgetServer.IsExistByClassificationId(budget.ClassificationId, userId);if (isExist){return Ok(new ResponseData<bool>(HttpStatusCode.Found, "用户已存在该类型预算", false));}Budget budgetDb = _mapper.Map<Budget>(budget);budgetDb.UserId = userId;budgetDb.CreateDateTime = DateTime.Now;_budgetServer.Add(budgetDb);return Ok(new ResponseData<bool>(HttpStatusCode.OK, "添加成功", true));}catch (Exception e){return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "添加失败", false));}}}
}

这段代码通过_budgetServer.IsExistByClassificationId调用检查该用户是否已经存在相同分类的预算。如果存在,方法立即返回一个状态码为HttpStatusCode.Found的响应,并提示用户预算已存在,同时返回false以指示操作失败。如果预算不存在,代码通过_mapper.Map<Budget>(budget)将前端传递的BudgetAddViewModel对象映射为Budget实体对象。这种映射通常通过AutoMapper等工具完成,简化了DTO(数据传输对象)与实体之间的转换。随后,给新预算实体赋值当前用户的ID以及创建时间,确保数据完整性。在完成数据准备后,调用_budgetServer.Add方法将预算添加到数据库中。
在使用_mapper.Map<Budget>(budget)进行数据转换时我们需要先在SporeAccountingProfile类中配置好转换关系,这里就不多讲了,不清楚的同学请参考专栏一开始的几篇文章,或者上AutoMapper官网学习。

三、总结

这篇文章我们一起编写的预算服务的新增功能,剩余的功能大家自己动手实现,然后下载我的代码来对比一下哪里不一样。
下一篇文章,我们将结合预算和记账功能来完成一个稍微复杂的业务:预算的回退和扣除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502066.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年1月4日蜻蜓q旗舰版st完整开源·包含前后端所有源文件·开源可商用可二开·优雅草科技·优雅草kir|优雅草星星|优雅草银满|优雅草undefined

2025年1月4日蜻蜓q旗舰版st完整开源包含前后端所有源文件开源可商用可二开优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined 产品介绍&#xff1a; 本产品主要贡献者优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined-青史留名&#xff0c;时光如川浪淘…

计算机网络练习题

学习这么多啦&#xff0c;那就简单写几个选择题巩固一下吧&#xff01; 1. 在IPv4分组各字段中&#xff0c;以下最适合携带隐藏信息的是&#xff08;D&#xff09; A、源IP地址 B、版本 C、TTL D、标识 2. OSI 参考模型中&#xff0c;数据链路层的主要功能是&#xff08;…

【UE5 C++课程系列笔记】21——弱指针的简单使用

目录 概念 声明和初始化 转换为共享指针 打破循环引用 弱指针使用警告 概念 在UE C 中&#xff0c;弱指针&#xff08;TWeakPtr &#xff09;也是一种智能指针类型&#xff0c;主要用于解决循环引用问题以及在不需要强引用保证对象始终有效的场景下&#xff0c;提供一种可…

Spring Boot 的自动配置,以rabbitmq为例,请详细说明

Spring Boot 的自动配置特性能够大大简化集成外部服务和组件的配置过程。以 RabbitMQ 为例&#xff0c;Spring Boot 通过 spring-boot-starter-amqp 提供了自动配置支持&#xff0c;开发者只需在应用中添加相关依赖并配置必要的属性&#xff0c;Spring Boot 会自动配置所需的连…

2025/1/4期末复习 密码学 按老师指点大纲复习

我们都要坚信&#xff0c;道路越是曲折&#xff0c;前途越是光明。 --------------------------------------------------------------------------------------------------------------------------------- 现代密码学 第五版 杨波 第一章 引言 1.1三大主动攻击 1.中断…

Vulnhub靶场(Earth)

项目地址 https://download.vulnhub.com/theplanets/Earth.ova.torrent 搭建靶机 官网下载.ova文件双击vm打开导入 获取靶机IP kail终端输入 arp-scan -l 获取靶机 IP 192.168.131.184 信息收集 端口扫描 sudo nmap -sC -sV -p- 192.168.131.184 可以看到开启22端口&…

Linux菜鸟级常用的基本指令和基础知识

前言:很多Linux初学者都会头疼于指令太多记不住&#xff0c;笔者刚学习Linux时也是如此&#xff0c;学习Linux指令时&#xff0c;学了后面的指令&#xff0c;前面的指令也会忘的差不多了&#xff0c;针对于以上这些情况&#xff0c;笔者今天来分享一篇Linux菜鸟级的常用指令的博…

使用SSH建立内网穿透,能够访问内网的web服务器

搞了一个晚上&#xff0c;终于建立了一个内网穿透。和AI配合&#xff0c;还是得自己思考&#xff0c;AI配合才能搞定&#xff0c;不思考只依赖AI也不行。内网服务器只是简单地使用了python -m http.server 8899&#xff0c;但是对于Gradio建立的服务器好像不行&#xff0c;会出…

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无

2024年1月4日蜻蜓hr人才招聘系统v1.1.7更新-正式版发布-客户端源代码开源发布供学习-本产品完成上线正式版-修复多个bug-优雅草果果|小无 前端代码开源库 关于开源说明&#xff1a;企业服务-招聘信息管理系统-前端uniapp-系统前端开放源代码仅供学习-优雅草科技-目前优雅草科…

HTML——75. 内联框架

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>内联框架</title><style type"text/css">iframe{width: 100%;height: 500px;}</style></head><body><!--iframe元素会创建包含…

Ajax原理-XMLHttpRequest

1. XMLHttpRequest 是什么&#xff1f; 和axios的关系&#xff1a; axios 内部采用 XMLHttpRequest 与服务器交互 学习XMLHttpRequest的目的&#xff1a; 掌握使用 XHR 与服务器进行数据交互&#xff0c;了解 axios 内部原理&#xff0c;加强对知识的理解&#xff0c;提升技…

离散数学 期末笔记

命题符号化 使用等值演算法证明 求公式范式 在自然推理体系中构造下列推理的证明 在一阶逻辑中将下列命题符号化 设A、B、C、D是 Z 的子集 证明下列集合恒等式 二元关系 性质 没有空的 没有漏的 没有重复 函数

Fabric环境部署-Git和Node安装

一.安装Git&#xff08;v2.43.0&#xff09; Git 是一个开源的分布式版本管理系统&#xff08;也是全球最大的开源软件存储服务器&#xff09;&#xff0c;用于敏捷高效地处理任何或小或大的项目。搭建区块链需要使用Git&#xff0c;因为区块链的开发和部署需要使用版本控制工…

springCloud 脚手架项目功能模块:Java分布式锁

文章目录 引言分布式锁产生的原因:集群常用的分布式锁分布式锁的三种实现方式I ZooKeeper 简介zookeeper本质上是一个分布式的小文件存储系zookeeper特性:全局数据一致性ZooKeeper的应用场景分布式锁(临时节点)II 基于ZooKeeper 实现一个排他锁创建锁获取锁释放锁Apache Zo…

stm32入门元件介绍

stm32入门元件介绍 文章目录 stm32入门元件介绍入门套件总览套件介绍面包板面包板跳线/飞线杜邦线STM32最小系统板STLINKOLED显示屏LED按键电位器蜂鸣器光敏/热敏电阻传感器/对射式/反射式红外传感器旋转编码器USB转串口MPU6050陀螺仪加速度计W25Q64 Flash闪存TB6612FNG电机驱动…

C语言:调试的概念和调试器的选择

所谓调试&#xff08;Dubug&#xff09;&#xff0c;就是跟踪程序的运行过程&#xff0c;从而发现程序的逻辑错误&#xff08;思路错误&#xff09;&#xff0c;或者隐藏的缺陷&#xff08;Bug&#xff09;。 在调试的过程中&#xff0c;我们可以监控程序的每一个细节&#xff…

Python深度学习GRU、LSTM 、BiLSTM-CNN神经网络空气质量指数AQI时间序列预测及机器学习分析|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p38742 分析师&#xff1a;Zhixiong Weng 人们每时每刻都离不开氧&#xff0c;并通过吸入空气而获得氧。一个成年人每天需要吸入空气达6500升以获得足够的氧气&#xff0c;因此&#xff0c;被污染了的空气对人体健康有直接的影响&…

Flink源码编译与运行

1 准备 准备好Java 8环境和编译器&#xff08;如IDEA&#xff09;。 下载源码&#xff1a; 官网&#xff1a;https://flink.apache.org/downloads/。GitHub&#xff1a;https://github.com/apache/flink。 2 编译 在IDEA终端&#xff0c;使用下面命令之一编译源码&#xff…

Elasticsearch:Lucene 2024 年回顾

作者&#xff1a;来自 Elastic Chris Hegarty 2024 年对于 Apache Lucene 来说又是重要的一年。在本篇博文中&#xff0c;我们将探讨主要亮点。 Apache Lucene 在 2024 年表现出色&#xff0c;发布了许多版本&#xff0c;包括三年来的首次重大更新&#xff0c;其中包含令人兴奋…

(四)基于STM32通过Event Recoder实现时间测量功能

目录 1. 了解Event Recorder 2. 硬件和软件准备 硬件需求 软件需求 3. 配置STM32工程 使用STM32CubeMX初始化项目 配置Event Recorder 4. 实现时间记录功能 初始化Event Recorder 时间间隔计算 配置Debug选项 测量结果查看 5总结 在嵌入式系统开发中&#xff0c;精…