19. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--收支记录

在本篇文章中,我们将一起编写孢子记账的收支记录功能(CURD),同样我们只列出一个具体功能的实现,剩下的功能由读者实现。

一、 需求

需求如下:

编号需求说明
1新增记录1.记录内容包括转换前金额、转换后金额、分类、日期、所属账本、币种、备注; 2.如果选择的币种不是设置的主币种,则将金额转换为主币种的金额
2删除记录1.不存在的记录给予提示
3修改记录1. 不存在的记录给予提示;2. 如果选择的币种不是设置的主币种,则将金额转换为主币种的金额
4查询记录1. 支持查询某一个记录;2. 支持根据开始时间和结束时间分页查询

就目前来说这个需求比较简单,一共5个接口,都是简单的CURD操作。并且根据需求我们也分析出来了收支记录表 IncomeExpenditureRecord 的主要结构:转换前金额 BeforAmount 、转换后金额 AfterAmount 、收支分类 IncomeExpenditureClassificationId 、记录日期 RecordDate、所属账本 AccountBookId、记录币种 CurrencyId、备注 Remark 。其中收支分类、所属账本以及记录币种都是外键,备注可以为空。同时这里要注意的是与金额相关的字段都要用decimal类型来作为字段类型。

二、功能编写

2.1 数据库映射类编写

根据前面的分析,我们的数据库映射类如下:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using SporeAccounting.BaseModels;namespace SporeAccounting.Models;/// <summary>
/// 收支记录表
/// </summary>
[Table("IncomeExpenditureRecord")]
public class IncomeExpenditureRecord : BaseModel
{/// <summary>/// 转换前金额/// </summary>[Column(TypeName = "decimal(18,2)")][Required]public decimal BeforAmount { get; set; }/// <summary>/// 转换后金额/// </summary>[Column(TypeName = "decimal(18,2)")][Required]public decimal AfterAmount { get; set; }/// <summary>/// 收支分类Id/// </summary>[Column(TypeName = "nvarchar(36)")][ForeignKey("FK_IncomeExpenditureRecord_IncomeExpenditureClassification")][Required]public string IncomeExpenditureClassificationId { get; set; }/// <summary>/// 记录日期/// </summary>[Column(TypeName = "datetime")][Required]public DateTime RecordDate { get; set; } = DateTime.Now;/// <summary>/// 账簿Id/// </summary>[Column(TypeName = "nvarchar(36)")][ForeignKey("FK_IncomeExpenditureRecord_AccountBook")][Required]public string AccountBookId { get; set; }/// <summary>/// 转换前币种Id/// </summary>[Column(TypeName = "nvarchar(36)")][ForeignKey("FK_IncomeExpenditureRecord_Currency")][Required]public string CurrencyId { get; set; }/// <summary>/// 备注/// </summary>[Column(TypeName = "nvarchar(100)")]public string? Remark { get; set; }/// <summary>/// 用户id/// </summary>[Column(TypeName = "nvarchar(36)")][ForeignKey("FK_IncomeExpenditureRecord_SysUser")][Required]public string UserId { get; set; }/// <summary>/// 导航属性/// </summary>public SysUser User { get; set; }/// <summary>/// 导航属性/// </summary>public Currency Currency { get; set; }/// <summary>/// 导航属性/// </summary>public IncomeExpenditureClassification IncomeExpenditureClassification { get; set; }/// <summary>/// 导航属性/// </summary>public AccountBook AccountBook { get; set; }
}

关于外键指向的类的导航属性的增加这里就不展示了,请各位读者自己根据前面文章所讲的内容,以及专栏《轻松学EntityFramework Core》中关于导航属性的内容,自己手动编写相关代码。

2.2 实现需求

这一小节我们一起编写新增收支记录的功能,其他功能大家自己独立编写,然后对比我的代码。由于收支记录服务接口及其实现类和前面其他功能的类大致一样,因此这里我也就不再展示代码了,只展示新增收支记录Action的代码。

using System.Net;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SporeAccounting.BaseModels;
using SporeAccounting.BaseModels.ViewModel.Response;
using SporeAccounting.Models;
using SporeAccounting.Models.ViewModels;
using SporeAccounting.Server.Interface;namespace SporeAccounting.Controllers
{/// <summary>/// 收支记录控制器/// </summary>[Route("api/[controller]")][ApiController]public class IncomeExpenditureRecordController : BaseController{/// <summary>/// 收支记录服务/// </summary>private readonly IIncomeExpenditureRecordServer _incomeExpenditureRecordServer;/// <summary>/// 配置服务/// </summary>private readonly IConfigServer _configServer;/// <summary>/// 汇率记录服务/// </summary>private readonly IExchangeRateRecordServer _exchangeRateRecordServer;/// <summary>/// 币种服务/// </summary>private readonly ICurrencyServer _currencyServer;/// <summary>/// 映射器/// </summary>private readonly IMapper _mapper;/// <summary>/// 构造函数/// </summary>/// <param name="incomeExpenditureRecordServer"></param>/// <param name="configServer"></param>/// <param name="exchangeRateRecordServer"></param>/// <param name="currencyServer"></param>/// <param name="mapper"></param>public IncomeExpenditureRecordController(IIncomeExpenditureRecordServer incomeExpenditureRecordServer,IConfigServer configServer,IExchangeRateRecordServer exchangeRateRecordServer,ICurrencyServer currencyServer,IMapper mapper){_incomeExpenditureRecordServer = incomeExpenditureRecordServer;_configServer = configServer;_exchangeRateRecordServer = exchangeRateRecordServer;_currencyServer = currencyServer;_mapper = mapper;}/// <summary>/// 添加收支记录/// </summary>/// <param name="incomeExpenditureRecordAddViewModel"></param>/// <returns></returns>[HttpPost][Route("Add")]public ActionResult<ResponseData<bool>> Add([FromBody] IncomeExpenditureRecordAddViewModel incomeExpenditureRecordAddViewModel){try{string userId = GetUserId();//获取用户设置的主币种Config? config = _configServer.Query(userId, ConfigTypeEnum.Currency);if (config == null){return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, "未设置主币种"));}// 如果选择的币种不是设置的主币种,则将金额转换为主币种的金额if (config.Value != incomeExpenditureRecordAddViewModel.CurrencyId){//查询主币种Currency? mainCurrency = _currencyServer.Query(config.Value);if (mainCurrency == null){return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, "币种不存在"));}// 查询传入的币种Currency? recordCurrency = _currencyServer.Query(incomeExpenditureRecordAddViewModel.CurrencyId);if (recordCurrency == null){return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, "币种不存在"));}//获取记录币种和主币种的汇率ExchangeRateRecord? exchangeRateRecord =_exchangeRateRecordServer.Query($"{mainCurrency.Abbreviation}_{recordCurrency.Abbreviation}");if (exchangeRateRecord == null){return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, "汇率不存在"));}incomeExpenditureRecordAddViewModel.AfterAmount *= exchangeRateRecord.ExchangeRate;}IncomeExpenditureRecord incomeExpenditureRecord =_mapper.Map<IncomeExpenditureRecord>(incomeExpenditureRecordAddViewModel);incomeExpenditureRecord.UserId = userId;incomeExpenditureRecord.CreateDateTime = DateTime.Now;incomeExpenditureRecord.CreateUserId = userId;_incomeExpenditureRecordServer.Add(incomeExpenditureRecord);return Ok(new ResponseData<bool>(HttpStatusCode.OK, data: true));}catch (Exception e){return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "服务端异常"));}}}
}

这段代码定义了一个控制器 IncomeExpenditureRecordController,用于管理与收支记录相关的操作。控制器依赖多个服务接口来完成其逻辑,包括收支记录服务、配置服务、汇率记录服务、币种服务和映射器。
在控制器的 Add 方法中,首先从请求体中接收 IncomeExpenditureRecordAddViewModel 类型的对象。接着,通过 GetUserId 方法获取当前用户的ID,用于后续的用户绑定。
然后,系统会检查用户是否设置了主币种配置,如果没有,则返回“未设置主币种”的响应。如果输入的币种与用户设置的主币种不同,系统会通过 _currencyServer 查询主币种和输入的币种信息。如果任一币种不存在,则返回“币种不存在”的响应。
如果币种存在,则通过 _exchangeRateRecordServer 查询这两个币种的汇率信息。如果找不到汇率记录,则返回“汇率不存在”的响应。若汇率存在,则将输入的金额根据汇率转换为主币种金额。
在所有数据准备完成后,使用 _mapper 将视图模型 IncomeExpenditureRecordAddViewModel 转换为 IncomeExpenditureRecord 实体对象,并设置与用户相关的属性(如 UserIdCreateUserId)。最终,通过 _incomeExpenditureRecordServer 将新记录保存到数据库,并返回操作成功的响应。 如果在过程中发生异常,则返回“服务端异常”的响应。

三、总结

这篇文章我们一起实现了收支记录中的新增记录功能,这功能比前面咱们实现的功能稍显复杂,需要获取主币种、获取主币种与用户选择的币种之间的汇率,最后根据汇率换算成主币种。
下一篇文章我们将结合主币种设置以及收支记录实现切换主币种后重新计算以前记录的转换后的金额。

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

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

相关文章

ubuntu22.04 使用crash

文章目录 前言一、apt 安装dbgsym vnlinux二、使用.ddeb包安装dbgsym vnlinux三、dbgsym发行版四、crash调试参考资料 前言 最近在适配 ubuntu系统&#xff0c;记录一下其crash的安装。 一、apt 安装dbgsym vnlinux # echo "deb http://ddebs.ubuntu.com $(lsb_release…

Edge SCDN 边缘安全加速有什么用?

Edge SCDN是最新推出的边缘安全加速服务&#xff0c;它是一种融合了安全防护和内容分发加速功能的网络服务技术&#xff0c;通过在网络边缘部署服务器节点&#xff0c;来优化内容的传输和用户的访问体验&#xff0c;同时保障网络安全。 抵御 DDoS 攻击&#xff1a; Edge SCDN …

EDA - Spring Boot构建基于事件驱动的消息系统

文章目录 概述事件驱动架构的基本概念工程结构Code创建事件和事件处理器创建事件总线创建消息通道和发送逻辑创建事件处理器消息持久化创建消息发送事件配置 Spring Boot 启动类测试消息消费运行项目 概述 在微服务架构和大规模分布式系统中&#xff0c;事件驱动架构&#xff…

智能GitHub Copilot副驾驶®提示和技巧

简介 智能 GitHub Copilot 副驾驶 代表了开发者历史上的一个重要里程碑工具。它象征着人工智能辅助编程新时代的开始&#xff0c;它显着提高了开发人员的生产力&#xff0c;提高了代码质量&#xff0c;并且还对开发团队的整体福祉产生了积极影响。随着智能 GitHub Copilot 副驾…

CAD C# 批量替换当前图中块、标注

本案例功能为选择当前文档中一个块&#xff08;旧块&#xff09;&#xff0c;然后选择新图元&#xff08;新块&#xff09;&#xff0c;运行插件后新块将替换图中所有的旧块。 效果如下&#xff1a; public static class Class1{//选取对象替换块定义[CommandMethod("TT&…

java 缓存篇2

缓存的部署方式 单机主从哨兵集群 特性主从&#xff08;Master-Slave&#xff09;哨兵&#xff08;Sentinel&#xff09;集群&#xff08;Cluster&#xff09;数据分片不支持不支持支持&#xff0c;基于 slot 进行水平分片高可用性部分支持&#xff08;手动故障转移&#xff…

SpringBoot快速使用

一些名词的碎碎念: 1> 俩种网络应用设计模式 C/S 客户端/服务器 B/S 浏览器/服务器 俩者对比: 2> 集群和分布式的概念 集群: 分布式: 例子: 一个公司有一个人身兼多职 集群: 招聘N个和上面这个人一样身兼多职 分布式: 招聘N个人,分担上面这个人的工作,进行工作的拆分. 工…

苹果公司即将为iPhone和智能家居改用自主研发的蓝牙和Wi-Fi芯片

美股快讯&#xff1a;苹果公司即将为iPhone和智能家居改用自主研发的蓝牙和Wi-Fi芯片 苹果公司计划从明年开始在其设备上改用国产芯片进行蓝牙和Wi-Fi连接&#xff0c;此举将逐步淘汰目前由博通提供的部分部件。这种代号为Proxima的芯片已经开发了数年&#xff0c;现在计划用于…

Linux 切换用户的两种方法

sudo -su user1 与 su - user1 都可以让当前用户切换到 user1 的身份执行命令或进入该用户的交互式 Shell。但它们在权限认证方式、环境变量继承和 Shell 初始化过程等方面存在一些差异。 权限认证方式 su - user1 su 是 “switch user” 的缩写&#xff0c;默认情况下需要你输…

快速理解分布式事务Seate基本知识

Seata是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式&#xff0c;为用户打造一站式的分布式解决方案。 一.Seate的三大角色 在 Seata 的架构中&#xff0c;一共有三个角色&#xff1a;…

前端项目初始化搭建(二)

一、使用 Vite 创建 Vue 3 TypeScript 项目 PS E:\web\cursor-project\web> npm create vitelatest yf-blog -- --template vue-ts> npx > create-vite yf-blog --template vue-tsScaffolding project in E:\web\cursor-project\web\yf-blog...Done. Now run:cd yf-…

SQL最佳实践:避免使用COUNT=0

如果你遇到类似下面的 SQL 查询&#xff1a; SELECT * FROM customer c WHERE 0 (SELECT COUNT(*)FROM orders oWHERE o.customer_id c.customer_id);意味着有人没有遵循 SQL 最佳实践。该语句的作用是查找没有下过订单的客户&#xff0c;其中子查询使用了 COUNT 函数统计客…

多模态大模型(二)——用Transformer Encoder和Decoder的方法(BLIP、CoCa、BEiTv3)

文章目录 BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 理解、生成我都要&#xff0c;一个很有效的、根据图片生成caption的工具1. BLIP的研究动机2. BLIP的模型结构3. CapFilt Model4. BLIP的训练过程 CoCa: C…

【理想汽车中科院】基于模仿学习的端到端自动驾驶数据缩放规律

论文: https://arxiv.org/pdf/2412.02689 项目: https://github.com/ucaszyp/Driving-Scaling-Law 0. 摘要 端到端自动驾驶范式因其可扩展性而最近吸引了大量关注。然而&#xff0c;现有方法受到现实世界数据规模有限的制约&#xff0c;这阻碍了对端到端自动驾驶相关扩展规律…

【工具介绍】可以批量查看LableMe标注的图像文件信息~

在图像处理和计算机视觉领域&#xff0c;LabelMe是一个广泛使用的图像标注工具&#xff0c;它帮助我们对图像中的物体进行精确的标注。但是&#xff0c;当标注完成后&#xff0c;我们常常需要一个工具来批量查看这些标注信息。 今天&#xff0c;我要介绍的这款exe程序&#xf…

链式栈的实现及其应用

目录 一、链式栈结构模型 二、链式栈的实现 2.1创建 2.2压栈 2.3出栈 2.4判断栈是否为空 2.5查看栈顶 2.6释放栈 三、应用 链式栈实际上就是基于链表&#xff0c;压栈和弹栈可分别看作头插和头删&#xff0c;链表尾部就是栈底&#xff0c;头指针就是栈顶指针 一、链式…

day12 接口测试 ——入门→精通→实战(1)

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、接口测试分类 1.1 内部接口&#xff1a; 1.2 外部接口&#xff1a; 2、目前接口架构设计 2.1、基于SOAP架构&#xff0c; 2.2、基于RPC架构&#xff0c; 2.3、基于RestFul架构&#xff0c; 2.3.1…

程序的调试

一名优秀的程序员也是一名出色的侦探&#xff0c;每一次调试都是尝试破案的过程 目录 前言 一、什么是调试&#xff1f; 二、调试 1.调试是什么 2.基本步骤 三、调试注意事项 1.怎么写出易于调试的代码 assert(断言) const 2.常见错误 总结 前言 主要是怎么调试&#xff0c;调…

FPGA实现GTP光口数据回环传输,基于Aurora 8b/10b编解码架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案 3、工程详细设计方案工程设计原理框图用户数据发送模块基于GTP高速接口的数据回环传输架构GTP IP 简介GTP 基本结构GTP 发送和接收…

如何快速切换更新电脑网络的ip地址

1.ADSL拨号更换IP地址 这种更换 IP地址的方法其实就是我们平时使用的宽带拨号&#xff0c;每次拨号得到的IP地址都不同&#xff0c;但是这种方法无法使用于光纤宽带&#xff0c;并且使用这种方法更换的IP地址&#xff0c;一般只会变更最后一个号段&#xff0c;前三个号段的数字…