24. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--预算扣除、退回、增补

[复制链接]
发表于 2025-11-9 08:34:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
这篇文章我们一起来编写如今为止最为复杂的功能:预算扣除、退回、增补。预算回退有三种情况:修改后的付出金额小于修改前的付出金额、付出记录删除后、记录范例从付出改为收入。预算增补的情况有两种:记录范例从收入改为付出、修改后的付出金额大于修改前的付出金额。
一、需求

根据前面所说的需求如下:
编号需求阐明1预算回退1. 修改后的付出金额小于修改前的付出金额;2. 付出记录删除后;3. 记录范例从付出改为收入2预算增补1. 记录范例从收入改为付出;2. 修改后的付出金额大于修改前的付出金额3预算扣除新增付出记录后扣除预算二、功能编写

我们一起来实现怎样实现预算退回与预算增补功能。这里要注意的是,我们将引入EF Core 的事故机制,如果你不知道EF Core的事故机制请先去学习专栏《轻松学EntityFramework Core
》
2.1 预算扣除

预算扣除功能最简朴,我们只必要在新增付出记录的同时扣除对应的预算即可。我们修改IncomeExpenditureRecordImp类下的Add方法,修改后的代码如下:
  1. /// <summary>
  2. /// 新增收支记录
  3. /// </summary>
  4. /// <param name="incomeExpenditureRecord"></param>
  5. public void Add(IncomeExpenditureRecord incomeExpenditureRecord)
  6. {
  7.     //开启事务
  8.     using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction())
  9.     {
  10.         try
  11.         {
  12.             // 查找记录范围内的预算
  13.             var budget = _sporeAccountingDbContext.Budgets
  14.                 .FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId
  15.                                      && x.StartTime <= incomeExpenditureRecord.RecordDate &&
  16.                                      x.EndTime >= incomeExpenditureRecord.RecordDate
  17.                                      && x.IncomeExpenditureClassificationId ==
  18.                                      incomeExpenditureRecord.IncomeExpenditureClassificationId);
  19.             if (budget != null)
  20.             {
  21.                 // 查询分类
  22.                 var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications
  23.                     .FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);
  24.                 if (classification.Type
  25.                     == IncomeExpenditureTypeEnmu.Income)
  26.                 {
  27.                     budget.Remaining -= incomeExpenditureRecord.AfterAmount;
  28.                 }
  29.                 _sporeAccountingDbContext.Budgets.Update(budget);
  30.             }
  31.             _sporeAccountingDbContext.IncomeExpenditureRecords.Add(incomeExpenditureRecord);
  32.             _sporeAccountingDbContext.SaveChanges();
  33.             //提交事务
  34.             transaction.Commit();
  35.         }
  36.         catch (Exception e)
  37.         {
  38.             //回滚事务
  39.             transaction.Rollback();
  40.             throw;
  41.         }
  42.     }
  43. }
复制代码
Add 方法的核心部门包罗事故处置惩罚、预算查找与更新、分类查询、记录添加和错误处置惩罚。起首,代码通过调用 _sporeAccountingDbContext.Database.BeginTransaction() 开启了一个数据库事故,以确保数据操纵的原子性。如果操纵乐成,事故会提交;如果发生非常,事故将回滚,克制数据不划一。接着,代码从数据库中查找符合条件的预算记录。预算查找逻辑基于 UserId 和记录日期范围,同时匹配 IncomeExpenditureClassificationId。如果找到相干预算,代码会进一步查询该收支记录的分类信息。然后,根据分类的范例举行处置惩罚。如果分类范例为收入IncomeExpenditureTypeEnmu.Income,代码将从预算的剩余金额中扣除收支记录的金额AfterAmount。紧接着代码将新的收支记录添加到 IncomeExpenditureRecords 聚集中,并调用 SaveChanges() 生存全部更改。末了,事故被提交。如果过程中出现任何非常,捕捉到的非常会触发事故回滚,确保数据库保持划一性并克制部门操纵被永世生存。回滚后,非常被重新抛出,以便调用者可以处置惩罚这个错误。通过这种方式,代码实现了对收支记录的安全、原子化操纵,确保数据划一性和完备性。
2.2 预算回退与增补

这里我们要修改IncomeExpenditureRecordImp类中的两个方法:Update和Delete。Delete方法很简朴,我们直接将付出记录的金额再加回预算即可,代码如下:
  1. /// <summary>
  2. /// 删除收支记录
  3. /// </summary>
  4. /// <param name="incomeExpenditureRecordId"></param>
  5. /// <returns></returns>
  6. public void Delete(string incomeExpenditureRecordId)
  7. {
  8.     //开启事务
  9.     using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction())
  10.     {
  11.         try
  12.         {
  13.             var incomeExpenditureRecord = _sporeAccountingDbContext.IncomeExpenditureRecords
  14.                 .FirstOrDefault(x => x.Id == incomeExpenditureRecordId);
  15.             if (incomeExpenditureRecord != null)
  16.             {
  17.                 // 查找记录范围内的预算
  18.                 var budget = _sporeAccountingDbContext.Budgets
  19.                     .FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId
  20.                                          && x.StartTime <= incomeExpenditureRecord.RecordDate &&
  21.                                          x.EndTime >= incomeExpenditureRecord.RecordDate
  22.                                          && x.IncomeExpenditureClassificationId == incomeExpenditureRecord
  23.                                              .IncomeExpenditureClassificationId);
  24.                 if (budget != null)
  25.                 {
  26.                     // 查询分类
  27.                     var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications
  28.                         .FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);
  29.                     if (classification.Type
  30.                         == IncomeExpenditureTypeEnmu.Income)
  31.                     {
  32.                         budget.Remaining += incomeExpenditureRecord.AfterAmount;
  33.                     }
  34.                     _sporeAccountingDbContext.Budgets.Update(budget);
  35.                 }
  36.                 _sporeAccountingDbContext.IncomeExpenditureRecords.Remove(incomeExpenditureRecord);
  37.                 _sporeAccountingDbContext.SaveChanges();
  38.                 //提交事务
  39.                 transaction.Commit();
  40.             }
  41.         }
  42.         catch (Exception e)
  43.         {
  44.             //回滚事务
  45.             transaction.Rollback();
  46.             throw;
  47.         }
  48.     }
  49. }
复制代码
Delete 方法起首开启一个数据库事故,确保在整个删除操纵过程中,如果出现任何题目,可以回滚全部更改以保持数据划一性。然后,通过查询 IncomeExpenditureRecords 聚集查找与给定 incomeExpenditureRecordId 匹配的收支记录。如果找到相应的记录,代码继承实验下一步。接下来,代码查找与该收支记录相干的预算信息。预算查询的条件包罗用户 ID、记录日期范围,以及收支分类 ID。如果找到匹配的预算,代码会进一步查找该收支记录的分类信息,以确定该记录是否属于收入范例。如果分类范例是收入,代码将从预算的剩余金额中增长这笔收支记录的金额。这一步调的目的是在删除收支记录时,规复预算中的剩余金额,从而保持预算数据的正确性。
完成预算调解后,代码将目的收支记录从 IncomeExpenditureRecords 聚集中移除,并调用 SaveChanges() 方法将全部更改生存到数据库。末了,事故被提交,确认全部操纵乐成完成。
Update 方法的修改必要将数据分身记录类分类的范例是否发生变更,代码如下:
  1. /// <summary>
  2. /// 修改收支记录
  3. /// </summary>
  4. /// <param name="incomeExpenditureRecord"></param>
  5. /// <returns></returns>
  6. public void Update(IncomeExpenditureRecord incomeExpenditureRecord)
  7. {
  8.     using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction())
  9.     {
  10.         try
  11.         {
  12.             // 查询原记录
  13.             var oldIncomeExpenditureRecord = _sporeAccountingDbContext.IncomeExpenditureRecords
  14.                 .FirstOrDefault(x => x.Id == incomeExpenditureRecord.Id);
  15.             // 查找记录范围内的预算
  16.             var budget = _sporeAccountingDbContext.Budgets
  17.                 .FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId
  18.                                      && x.StartTime <= incomeExpenditureRecord.RecordDate &&
  19.                                      x.EndTime >= incomeExpenditureRecord.RecordDate
  20.                                      && x.IncomeExpenditureClassificationId ==
  21.                                      incomeExpenditureRecord.IncomeExpenditureClassificationId);
  22.             if (budget != null)
  23.             {
  24.                 // 查询分类
  25.                 var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications
  26.                     .FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);
  27.                 if (classification.Type
  28.                     == IncomeExpenditureTypeEnmu.Income)
  29.                 {
  30.                     //如果是支出,需要减去原来的金额
  31.                     budget.Remaining = (budget.Amount - incomeExpenditureRecord.AfterAmount);
  32.                 }
  33.                 else
  34.                 {
  35.                     //如果是收入,需要加上原来的金额
  36.                     budget.Remaining = (budget.Amount + incomeExpenditureRecord.AfterAmount);
  37.                 }
  38.                 _sporeAccountingDbContext.Budgets.Update(budget);
  39.             }
  40.             oldIncomeExpenditureRecord.AfterAmount = incomeExpenditureRecord.AfterAmount;
  41.             oldIncomeExpenditureRecord.BeforAmount = incomeExpenditureRecord.BeforAmount;
  42.             oldIncomeExpenditureRecord.RecordDate = incomeExpenditureRecord.RecordDate;
  43.             oldIncomeExpenditureRecord.Remark = incomeExpenditureRecord.Remark;
  44.             oldIncomeExpenditureRecord.IncomeExpenditureClassificationId =
  45.                 incomeExpenditureRecord.IncomeExpenditureClassificationId;
  46.             _sporeAccountingDbContext.IncomeExpenditureRecords.Update(oldIncomeExpenditureRecord);
  47.             _sporeAccountingDbContext.SaveChanges();
  48.             //提交事务
  49.             transaction.Commit();
  50.         }
  51.         catch (Exception e)
  52.         {
  53.             //回滚事务
  54.             transaction.Rollback();
  55.             throw;
  56.         }
  57.     }
  58. }
复制代码
Update 方法通过 IncomeExpenditureRecords 聚集查找与提供的记录 ID 匹配的原始收支记录。找到后,代码继承查找与这条记录相干的预算信息。预算查询条件包罗用户 ID、记录日期范围和收支分类 ID。如果找到匹配的预算,代码接着通过 IncomeExpenditureClassifications 聚集查询记录的分类信息。根据分类范例(收入或付出),代码调解预算的剩余金额。如果分类是收入,代码从预算金额中减去更新后的金额;如果是付出,代码将金额加回预算。这确保了预算在更新记录后仍然正确。之后,代码更新原始收支记录的相干属性,包罗金额、日期、备注和分类 ID。将修改后的记录通过 Update 方法标记为必要更新,并调用 SaveChanges() 生存全部更改。
三、总结

文章先容了预算管理中复杂功能的实现,包罗预算扣除、退回和增补。预算回退涉及三种情况:修改后的付出金额小于修改前的金额、删除付出记录、记录范例从付出改为收入;预算增补包罗记录范例从收入改为付出以及修改后的付出金额大于修改前的金额。文章具体形貌了怎样在新增、删除和修改收支记录时,使用 EF Core 事故机制确保操纵的原子性和数据划一性。具体实现方法包罗调解预算金额、根据收支记录范例动态更新预算、处置惩罚非常并回滚事故,以包管数据库的完备性和准确性。文章通过实际代码示例清晰地论述了这些操纵的细节。
下一篇文章,我们将连合主币种设置和预算来实现预算金额的币种转换。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表