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.mzph.cn/news/891734.shtml

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

相关文章

Pentaho Kettle迁移至Oracle的空字符串和NULL的问题处理,大坑!

一、问题说明 在使用 Kettle 将 DB2 数据迁移到 Oracle 的过程中&#xff0c;出现了 DB2 中为空字符串的字段&#xff0c;在插入到 Oracle 过程中实际插入的为 NULL &#xff0c;导致触发了非空校验而迁移失败 空字符串 ‘’ &#xff0c;即长度为0的字符串 搜索该问题后得知…

2025-01-04 Unity插件 YodaSheet1 —— 插件介绍

文章目录 1 介绍2 工作原理2.1 ScriptableObject -> YadeSheetData2.2 YadeDatabase 存储多个 YadeSheetData 3 用途4 缺点5 推荐 1 介绍 ​ Yade 提供类似于 Excel 或者 Google Sheets 的表格编辑器&#xff0c;可以轻松地在 Unity 编辑器中 编辑&#xff0c;搜索&#xf…

VScode 格式化代码空格记录

点击 -> “文件” -> “首选项" -> “设置” -> 按下图操作&#xff1a; 怎么格式化代码空格&#xff0c;先看下&#xff1a; 保存代码后&#xff0c;这代码自动格式化发&#xff0c;如下图&#xff1a; 你可以试试看就即可

苹果系统MacOS下ObjectC建立的App程序访问opencv加载图片程序

前言 苹果系统下使用opencv感觉还是有些不太方便&#xff0c;总是感觉有点受到限制。本博客描述的是在MacOS下建立App程序然后调用opencv显示图片时出现的一些问题并最后解决的一个过程。 一、程序的建立 选择程序的类型&#xff1a; 选择界面模式和编程语言&#xff1a; 其余…

CSS3——3. 书写格式二

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><!--css书写&#xff1a;--><!--1. 属性名:属性值--><!--2.属性值是对属性的相关描述--><!--3.属性名必须是…

windows配置jdk

进入此网址 JDK 11 Releases 找到11版本进行下载 下载好后配置环境变量 按windows加r键输入sysdm.cpl点击确定 在系统变量里新建输入 JAVA_HOME D:\openjdk-1128_windows-x64_bin\jdk-11 //变量值为安装jidk的路径 配置path环境变量 编辑path变量输入%JAVA_HOME%\bin并放置最…

深入刨析数据结构之排序(下)

目录 1.内部排序 1.5选择排序 1.5.1简单选择排序 1.5.2树形选择排序 1.6堆排序 1.7归并排序 1.7.1递归归并 1.7.2非递归归并 1.8计数排序 1.9基数排序 常见内部排序的总结&#xff1a; 1.内部排序 1.5选择排序 选择排序&#xff08;Selection Sort&#xff09;的基…

RocketMQ场景问题

1.消息丢失 有这么一个场景&#xff0c;就是订单支付完成之后&#xff0c;订单系统会进行发送消息给RocketMQ集群&#xff0c;下游会有积分系统进行监听这个消息&#xff0c;进行消费然后给用户发放积分。在下面的这个场景中&#xff0c;通过查询日志发现了订单系统发送订单支付…

cordova项目环境搭建 hello

环境准备&#xff1a; 1.下载nodejs并安装配置。 直接官网下载最新版本。此次我是下载的 Node.js v22.12.0。 1.1安装好后配置镜像。 输入&#xff1a;node -v // 显示node.js版本 npm -v // 显示npm版本 1.2环境配置 &#xff08;1&#xff09;找到安装的目录&am…

LE Audio 初探

LE Audio 架构 一.LE Audio profile框架 profile初识&#xff1a; BAP&#xff1a; BAP通常在手机端实现&#xff0c;它允许配置编解码器&#xff0c;配置QoS(质量服务)&#xff0c;控制流媒体&#xff0c; PACS&#xff1a; Published Audio Capabilities Service&#xff0c;…

Ⅱ.INTRODUCTION TO CUDA C

前言 上一节环境配置好了&#xff0c;我们开始吧&#xff01; 一、A First Program 1. Hello, World! 我们先写一个C语言的 Hello, World! 作为对比 int main(void){printf("Hello, World!\n");return 0; }大家应该知道这个代码运行在CPU上吧&#xff0c;我们CP…

域上的多项式环,整除,相通,互质

例1.已知 (R,,x)为域&#xff0c;请选出正确的说法:(A)(R,,x)也是整区; ABCD (B)R中无零因子; C)R在x运算上满足第一、二、三指数律; (D)R只有平凡理想; (E)R只有平凡子环。 域的特征&#xff1a; 域中&#xff0c;非0元素的加法周期 思考、在模7整数环R,中&#xff0c;…

【0x0014】HCI_Read_Local_Name命令详解

目录 一、命令概述 二、命令格式 三、返回事件及参数说明 3.1. HCI_Command_Complete 事件 3.2. Status 3.3. Local_Name 四、命令执行流程 4.1. 命令发送 4.2. 控制器接收并处理命令 4.3. 控制器返回结果 4.4. 主机接收并解析事件包 4.5. 示例代码 五、应用场景 …

【AI日记】25.01.04 kaggle 比赛 3-3 | 王慧玲与基层女性

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Forecasting Sticker Sales时间&#xff1a;6 小时 读书 书名&#xff1a;基层女性时间&#xff1a;3 小时原因&#xff1a;虽然我之前就知道这个作者&#xff0c;因为我…

【从零开始入门unity游戏开发之——C#篇40】C#特性(Attributes)和自定义特性

文章目录 前言一、特性&#xff08;Attributes&#xff09;基本概念二、自定义特性1、自定义特性代码示例&#xff1a;2、应用自定义特性&#xff1a;3、解释3.1 **AttributeUsage 特性**3.2 特性的命名3.3 **构造函数**&#xff1a;3.4 **属性**&#xff1a; 4、使用反射获取特…

【Python学习(六)——While、for、循环控制、指数爆炸】

Python学习&#xff08;六&#xff09;——While、for、循环控制、指数爆炸 本文介绍了While、for、循环控制、指数爆炸&#xff0c;仅作为本人学习时记录&#xff0c;感兴趣的初学者可以一起看看&#xff0c;欢迎评论区讨论&#xff0c;一起加油鸭~~~ 心中默念&#xff1a;Py…

基于PyQt5的UI界面开发——图像与视频的加载与显示

介绍 这里我们的主要目标是实现一个基于PyQt5和OpenCV的图像浏览和视频播放应用。用户可以选择本地的图像或视频文件夹&#xff0c;进行图像自动播放和图像切换以及视频播放和调用摄像头等操作&#xff0c;并且支持图像保存功能。项目的核心设计包括文件路径选择、图像或视频的…

云手机+Facebook:让科技与娱乐完美结合

移动互联网时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现&#xff0c;则为Facebook营销注入了新的活力&#xff0c;其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…

全新免押租赁系统打造便捷安全的租赁体验

内容概要 全新免押租赁系统的推出&#xff0c;标志着租赁行业的一次重大变革。这个系统的最大特点就是“免押金”&#xff0c;大大减轻了用户在租赁过程中的经济负担。从此&#xff0c;不再需要为一部手机或其他商品支付高昂的押金&#xff0c;用户只需通过简单的信用评估&…

postman在软件测试中的应用

postman工具概述 Postman 是一款功能强大的 API 开发和测试工具&#xff0c;在软件开发和测试领域应用广泛。开发阶段&#xff0c;可以通过工具进行mock数据测试&#xff0c;方便开发&#xff0c;联调&#xff1b;测试阶段&#xff0c;可以通过不同环境&#xff0c;不同数据进…