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

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

这一篇我们来一起为账本功能编写代码。账本功能的代码很简单,就是一些简单的CURD操作。

一、需求

我们先来看一下需求:

编号需求说明
1新增账本1. 账本名称不能和用户已有的账本名称重复
2删除账本1. 存在收支记录的账本不能删除
3修改账本1. 修改的账本名称不能和用户已有的账本名称重复
4查询站本1. 支持按照账本名称、账本备注进行分页查询; 2. 支持根据账本id查询

根据上面的需求我们可以分析出5个接口和数据库表映射类所需的字段。5个接口就不多说了,需求上写的清清楚楚,这一小节我们重点看一下数据库表映射类所需的字段。
从需求1和3中分析出我们需要一个账本类来作为数据库表映射类,并且这个类需要有账本名称,以及账本所属用户的Id,从需求4中得知账本类还需要有备注,同时还需要另一个数据库表映射类收支记录类。
需求我们分析完了,现在我们就一起来编写实现账本功能的代码吧。和前面的文章一样,在这里我只带领大家实现删除功能,剩余的功能大家自己动手实现。

二、功能编写

2.1 创建数据库映射类

这里我们需要两个数据库映射类:账本类(AccountBook)、收支记录类(IncomeExpenditureRecordIncomeExpenditureRecord 类我们会在下一篇文章中实现,因此在这篇文章中我们只需要把它创建出来并在里面加上AccountBook 类的导航属性即可。代码如下:

  1. AccountBook
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.Build.Framework;
    using SporeAccounting.BaseModels;
    
    namespace SporeAccounting.Models;
    
    /// <summary>
    /// 账簿
    /// </summary>
    [Table(name: "AccountBook")]
    public class AccountBook : BaseModel
    {
        /// <summary>
        /// 账簿名称
        /// </summary>
        [Column(TypeName = "nvarchar(20)")]
        [Required]
        public string Name { get; set; }
    
        /// <summary>
        /// 备注
        /// </summary>
        [Column(TypeName = "nvarchar(100)")]
        public string? Remarks { get; set; }
    
        /// <summary>
        /// 用户Id
        /// </summary>
        [Column(TypeName = "nvarchar(36)")]
        [ForeignKey("FK_AccountBook_SysUser")]
        [Required]
        public string UserId { get; set; }
    
        /// <summary>
        /// 导航属性
        /// </summary>
        public SysUser User { get; set; }
    
        /// <summary>
        /// 导航属性
        /// </summary>
        public ICollection<IncomeExpenditureRecord> IncomeExpenditureRecords { get; set; } =
            new List<IncomeExpenditureRecord>();
    }
    
  2. IncomeExpenditureRecord
    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>
        public AccountBook AccountBook { get; set; }
    }
    

Tip:不明白什么是导航属性的,请先学习我写的专栏《轻松学EntityFramework Core

2.2 实现需求
  1. 创建AccountBook表数据库操作服务
    我们新建** 账本服务接口**IAccountBookServer以及他的实现类AccountBookImp ,并在添加Delete 方法、IsExistById 方法和判断账本是否存在收支记录的方法IsExistIncomeExpenditureRecord
    ///IAccountBookServer
    using SporeAccounting.Models;
    
    namespace SporeAccounting.Server.Interface;
    
    /// <summary>
    /// 账本服务接口
    /// </summary>
    public interface IAccountBookServer
    {
        /// <summary>
        /// 删除账本
        /// </summary>
        /// <param name="accountBookId"></param>
        void Delete(string accountBookId);
    
        /// <summary>
        /// 账本是否存在
        /// </summary>
        /// <param name="accountBookId"></param>
        /// <returns></returns>
        bool IsExistById(string accountBookId);
    
        /// <summary>
        /// 账本是否存在收支记录
        /// </summary>
        /// <param name="accountBookId"></param>
        /// <returns></returns>
        bool IsExistIncomeExpenditureRecord(string accountBookId);
    }
    
    
    
    ///AccountBookImp 
    using SporeAccounting.Models;
    using SporeAccounting.Server.Interface;
    
    namespace SporeAccounting.Server;
    
    /// <summary>
    /// 账本服务实现
    /// </summary>
    public class AccountBookImp : IAccountBookServer
    {
        /// <summary>
        /// 数据库上下文
        /// </summary>
        private readonly SporeAccountingDBContext _sporeAccountingDbContext;
    
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="sporeAccountingDbContext"></param>
        public AccountBookImp(SporeAccountingDBContext sporeAccountingDbContext)
        {
            _sporeAccountingDbContext = sporeAccountingDbContext;
        }
    
        /// <summary>
        /// 删除账本
        /// </summary>
        /// <param name="accountBookId"></param>
        public void Delete(string accountBookId)
        {
            try
            {
                AccountBook accountBook = _sporeAccountingDbContext.AccountBooks
                    .FirstOrDefault(p => p.Id == accountBookId)!;
                _sporeAccountingDbContext.AccountBooks.Remove(accountBook);
                _sporeAccountingDbContext.SaveChanges();
            }
            catch (Exception e)
            {
                throw;
            }
        }
    
        /// <summary>
        /// 账本是否存在
        /// </summary>
        /// <param name="accountBookId"></param>
        /// <returns></returns>
        public bool IsExistById(string accountBookId)
        {
            try
            {
                return _sporeAccountingDbContext.AccountBooks
                    .Any(p => p.Id == accountBookId);
            }
            catch (Exception e)
            {
                throw;
            }
        }
    
        /// <summary>
        /// 账本是否存在收支记录
        /// </summary>
        /// <param name="accountBookId"></param>
        /// <returns></returns>
        public bool IsExistIncomeExpenditureRecord(string accountBookId)
        {
            try
            {
                return _sporeAccountingDbContext.AccountBooks
                    .Include(p => p.IncomeExpenditureRecords)
                    .Any(p => p.Id == accountBookId);
                
                // 也可以这么查询
                // return _sporeAccountingDbContext.IncomeExpenditureRecords
                //     .Any(p => p.Id == accountBookId);
            }
            catch (Exception e)
            {
                throw;
            }
        }
    }
    
    IsExistIncomeExpenditureRecord 方法中的注释提供了一个替代实现,直接从 IncomeExpenditureRecords 表进行查询,跳过了通过 AccountBooks 的关联导航加载。这种方式可能效率更高,具体选择依赖业务需求。
3.2 Controller 实现

新建AccountBookController控制器,并添加Delete Action,代码如下:

using System.Net;
using AutoMapper;
using SporeAccounting.BaseModels.ViewModel.Response;
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 AccountBookController : BaseController
    {
        /// <summary>
        /// 账本服务
        /// </summary>
        private readonly IAccountBookServer _accountBookServer;

        /// <summary>
        /// 映射
        /// </summary>
        private readonly IMapper _mapper;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accountBookServer"></param>
        /// <param name="mapper"></param>
        public AccountBookController(IAccountBookServer accountBookServer, IMapper mapper)
        {
            _accountBookServer = accountBookServer;
            _mapper = mapper;
        }


        /// <summary>
        /// 删除账本
        /// </summary>
        /// <param name="accountBookId"></param>
        /// <returns></returns>
        [HttpDelete]
        [Route("Delete/{accountBookId}")]
        public ActionResult<ResponseData<bool>> Delete([FromRoute] string accountBookId)
        {
            try
            {
                //是否存在账本
                bool isExist = _accountBookServer.IsExistById(accountBookId);
                if (!isExist)
                {
                    return Ok(new ResponseData<bool>(HttpStatusCode.BadRequest, errorMessage: "账本不存在"));
                }

                //是否存在收支记录
                bool isExistIncomeExpenditureRecord = _accountBookServer.IsExistIncomeExpenditureRecord(accountBookId);
                if (isExistIncomeExpenditureRecord)
                {
                    return Ok(new ResponseData<bool>(HttpStatusCode.BadRequest, errorMessage: "账本存在收支记录,不能删除"));
                }

                _accountBookServer.Delete(accountBookId);
                return Ok(new ResponseData<bool>(HttpStatusCode.OK, data: true));
            }
            catch (Exception e)
            {
                return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, errorMessage: "服务端异常"));
            }
        }

    }
}

这段代码定义了一个名为 AccountBookController 的控制器,用于管理账本操作。该控制器继承自 BaseController,并通过依赖注入方式初始化了两个服务:账本服务 _accountBookServer 和映射服务 _mapper。控制器的路由前缀设置为 api/AccountBook,且标记为 API 控制器,确保符合 RESTful API 的约定。
Delete 方法实现了通过账本 ID 删除账本的逻辑,绑定了 DELETE 请求,并在路由中包含动态参数 accountBookId。方法首先调用账本服务检查指定 ID 的账本是否存在,若不存在则返回带有 400 Bad Request 状态码的响应数据,并附上错误信息。接着检查该账本是否存在收支记录,若存在也返回类似的错误响应,提示无法删除。若上述条件均不满足,则调用账本服务的 Delete 方法删除该账本,并返回成功响应,包含 200 OK 状态码和 true 的数据表示操作成功。在操作过程中若出现异常,捕获后返回服务器错误响应,附带 500 Internal Server Error 状态码和异常提示。

三、总结

这篇文章带领大家一起实现了账本的删除功能,代码比较简单,只要充分了解需求后就能编写出来。账本剩余的功能代码,大家自己动手实现。


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

相关文章:

  • 【前端】Next.js 服务器端渲染(SSR)与客户端渲染(CSR)的最佳实践
  • 做异端中的异端 -- Emacs裸奔之路1: Vim vs Emacs
  • 华为IPD流程学习之——深入解读123页华为IPD流程体系设计方法论PPT
  • HarmonyOS(60)性能优化之状态管理最佳实践
  • Spring Web MVC(详解中)
  • 视频汇聚平台Liveweb国标GB28181视频平台监控中心设计
  • eBay 基于 Celeborn RESTful API 进行自动化工具集成实践
  • Flink四大基石之CheckPoint
  • 计算机网络:数据链路层(二)
  • Milvus×Florence:一文读懂如何构建多任务视觉模型
  • 矩阵重构——reshape函数
  • Vue 3 组件通信教程
  • 不同云计算网络安全等级
  • HTTPTomcatServlet
  • Node报错:npm error code ETIMEDOUT
  • 智能合约开发框架--Hardhat
  • 电商数据采集电商行业数据分析电商平台数据获取|保障稳定的API接口数据
  • 如何在CodeIgniter中调用构造函数
  • DataOps 体系对企业数据管理价值及落地的探讨
  • 合并视频文件:使用Python和MoviePy库的简单指南
  • Python 视频合并工具
  • 乐鑫发布 esp-iot-solution v2.0 版本
  • RPC——Remote Procedure Call(远程过程调用)
  • I/O流程图
  • 大数据新视界 -- 大数据大厂之 Hive 数据质量保障:数据清洗与验证的策略(上)(17/ 30)
  • Web开发基础学习——HTTP的理解