欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 18. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--账本

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

2024/11/30 0:56:25 来源:https://blog.csdn.net/gangzhucoll/article/details/144030507  浏览:    关键词: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 状态码和异常提示。

三、总结

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com