Tai.Wallet.EntityFrameworkCore
0.1.8
See the version list below for details.
dotnet add package Tai.Wallet.EntityFrameworkCore --version 0.1.8
NuGet\Install-Package Tai.Wallet.EntityFrameworkCore -Version 0.1.8
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" Version="0.1.8" />
<PackageVersion Include="Tai.Wallet.EntityFrameworkCore" Version="0.1.8" />
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" />
paket add Tai.Wallet.EntityFrameworkCore --version 0.1.8
#r "nuget: Tai.Wallet.EntityFrameworkCore, 0.1.8"
#:package Tai.Wallet.EntityFrameworkCore@0.1.8
#addin nuget:?package=Tai.Wallet.EntityFrameworkCore&version=0.1.8
#tool nuget:?package=Tai.Wallet.EntityFrameworkCore&version=0.1.8
Tai.Wallet
ABP 10.6.1 / .NET 10 可复用钱包模块,提供用户或租户余额、资金流水与提现状态管理。核心模块不依赖数据库驱动、身份系统或支付渠道实现;消费宿主负责认证、权限授予、数据库迁移和实际付款。
项目与安装
| 包 | 用途 |
|---|---|
| Tai.Wallet.Domain.Shared | 枚举、约束、错误码与中英本地化 |
| Tai.Wallet.Domain | 余额、流水、提现与 BalanceManager |
| Tai.Wallet.Application.Contracts | DTO、IBalanceAppService 与权限定义 |
| Tai.Wallet.Application | 本人钱包查询、提现申请和取消 |
| Tai.Wallet.EntityFrameworkCore | 模型配置与四个自定义仓储 |
| Tai.Wallet.MongoDB | 历史模板,尚未实现钱包仓储,不可作为资金持久化实现 |
宿主安装所需包,并在模块类的 DependsOn 中声明 TaiWalletApplicationModule 与 TaiWalletEntityFrameworkCoreModule;进程内集成可仅使用 Domain 与 EntityFrameworkCore。HTTP 暴露由宿主通过 ABP 常规控制器配置完成。模块没有匿名业务入口,也没有开放客户端充值、完成提现的接口。
MySQL 示例使用 MySql.EntityFrameworkCore 10.0.7,PostgreSQL 示例使用 Npgsql.EntityFrameworkCore.PostgreSQL 10.0.0。驱动仅位于示例与测试项目;核心数据层保持数据库无关。
资金规则
金额使用有符号 long 整数单位,宿主必须统一单位和币种,例如全部以分计量;模块不做多币种转换。入账为正、出账为负,手续费非负且总是从余额扣除。流水记录的 AfterBalance 是扣除手续费后的实际余额。
- 余额不允许为负,计算使用 checked;拒绝交易不会改变余额。
- 提现金额必须大于零;
UserFee不大于Fee。申请时扣除Amount + UserFee,取消或失败时全额返还该值;渠道承担的手续费不计入用户余额。 - 禁用钱包拒绝普通交易,但已有提现的取消和失败仍能退款。
Created可取消、进入Pending、成功或失败;Pending只能成功或失败。终态禁止改为其他状态。BalanceManager管理资金变更;余额、流水和提现在同一 ABP 事务工作单元提交。已有非事务工作单元不能用于资金操作,会返回Tai.Wallet:TransactionRequired。- ABP 的
ConcurrencyStamp阻止并发覆盖。并发或唯一键冲突时让整个工作单元回滚,在新的工作单元中重试完整请求;不要捕获异常后继续提交原工作单元。
Transaction.Id 和 Withdrawal.Id 是幂等键。重复键与相同参数返回成功,重复键与不同参数返回 Tai.Wallet:RequestConflict。取消、成功、失败和进入处理中的相同状态请求可重复调用,不会重复扣款或退款。幂等记录保存在数据库,不依赖进程内锁。首次并发创建仍可能返回唯一键或并发冲突,调用方须按上述方式重试。
余额的 OwnerKey 为非空的租户与用户组合,唯一索引兼容 MySQL/PostgreSQL 对 NULL 的处理。保留软删除余额的唯一身份,避免删除后重新创建空余额。流水和提现不使用软删除。宿主不应直接修改或删除资金流水;这不是具备数据库级防篡改能力的复式记账系统。
应用契约和权限
所有应用服务要求登录以及 Wallet.Balances 权限。提现和取消还分别要求 Wallet.Balances.CreateWithdrawal、Wallet.Balances.CancelWithdrawal,并校验钱包确实属于当前用户和当前租户。权限不能绕过所有权。宿主通过自己的 ABP 权限管理模块授予这些权限。
var balance = await balanceAppService.GetAsync();
var requestId = Guid.NewGuid(); // 网络重试必须复用此 ID。
await balanceAppService.CreateWithdrawalAsync(new CreateWithdrawalInput
{
RequestId = requestId,
BalanceId = balance.Id,
Amount = 3000,
Channel = WalletChannel.Wechat
});
await balanceAppService.UpdateWithdrawalAsync(new UpdateWithdrawalInput
{
WithdrawalId = requestId,
State = WithdrawalState.Canceled
});
GetAsync 首次读取时创建本人钱包;第一次并发读取可能发生唯一键冲突,需要新工作单元重试。查询他人的余额和流水不会因管理员权限自动开放。交易列表只接受 Id、CreationTime、Amount、Fee、AbsoluteAmount、AfterBalance、Type、CategoryId 的 asc/desc 排序组合,分页最多 1000 条;同值排序以 Id 保持稳定顺序。
实际付款由宿主的可信后台集成调用 MarkWithdrawalPendingAsync、SucceedWithdrawalAsync 或 FailWithdrawalAsync。外部调用与数据库提交不能依靠本地事务实现原子性,宿主须使用渠道幂等键与对账,必要时配置 ABP Outbox/Inbox。客户端不允许标记提现成功。模块当前没有真实付款 Provider、对账、币种、转账双边记账或监管报表。
宿主 DbContext 接入
宿主 DbContext 必须实现 IWalletDbContext 并声明全部四个 DbSet,包括 Withdrawals,以便 ABP 在模型初始化时配置租户过滤与实体约定。
public class AppDbContext : AbpDbContext<AppDbContext>, IWalletDbContext
{
public DbSet<Balance> Balances { get; set; }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Withdrawal> Withdrawals { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureWallet();
}
}
注册宿主上下文并替换接口:
context.Services.AddAbpDbContext<AppDbContext>(options =>
{
options.ReplaceDbContext<IWalletDbContext>();
});
数据库驱动由宿主配置。宿主必须配置 Configure<AbpClockOptions>(options => options.Kind = DateTimeKind.Utc),统一审计和状态时间;PostgreSQL 的 timestamp with time zone 不接受 Local 时间。连接字符串名称为 Wallet,表前缀和 Schema 由 WalletDbProperties 控制;ConfigureWallet 也接受模型选项。每个租户的资金操作必须在正确的 CurrentTenant.Change(tenantId) 作用域执行,实体租户来自可信上下文。不会默认关闭租户过滤器。
构建与验证
从 wallet 目录执行:
dotnet tool restore
dotnet restore Tai.Wallet.slnx
dotnet build Tai.Wallet.slnx -c Release
dotnet test Tai.Wallet.slnx -c Release --no-build
bash scripts/verify.sh
默认业务测试使用内存 SQLite;MongoDB 历史测试仅检查驱动连接。竞争事务测试在 SQLite 中明确跳过,应分别执行 MySQL 与 PostgreSQL 验证:
WALLET_TEST_PROVIDER=postgres \
WALLET_TEST_CONNECTION='Host=localhost;Database=wallet_tests;Username=wallet;Password=CHANGE_ME' \
bash scripts/verify-relational.sh
WALLET_TEST_PROVIDER=mysql \
WALLET_TEST_CONNECTION='Server=localhost;Database=wallet_tests;User=wallet;Password=CHANGE_ME' \
bash scripts/verify-relational.sh
关系数据库测试会创建并删除名称为 wallet_test_<随机ID> 的隔离数据库;脚本还会在 wallet_migration_<随机ID> 中执行示例迁移与资金流程并删除该数据库。测试账号需要创建数据库权限;使用专用测试实例。脚本同样测试真实权限、租户隔离、金额边界、幂等、回滚与并发;不以跳过授权来换取通过。
示例是控制台宿主,演示真实仓储组合与迁移,不包含 HTTP 认证配置或权限绕过。使用独立示例数据库:
export Wallet__Provider=postgres # 或 mysql
export ConnectionStrings__Wallet='Host=localhost;Database=wallet_sample;Username=wallet;Password=CHANGE_ME'
dotnet run --project samples/Tai.Wallet.Sample -c Release -- --migrate
dotnet run --project samples/Tai.Wallet.Sample -c Release -- --smoke
MySQL 连接字符串使用 Server=localhost;Database=wallet_sample;User=wallet;Password=CHANGE_ME。两个数据库有独立的迁移与 ModelSnapshot,均位于示例宿主。迁移只通过显式 --migrate 执行,--smoke 会写入一笔演示充值、提现与退款。
此次重构的兼容性变化
尚未投产,因此直接修正以下契约和模型:Balance 构造函数显式接收 ID;UserId 只能由领域内部设置;提现输入增加必填 RequestId;更新提现使用 WithdrawalId;流水 DTO 增加 Id;余额 DTO 增加 IsDisabled;流水和提现增加 TenantId 并移除软删除字段;余额增加 OwnerKey;增加唯一索引、查询复合索引与 Restrict 外键;应用权限由未定义改为显式定义。简单 DTO 映射改为显式映射,移除 AutoMapper 依赖。
示例迁移为新的空数据库基线,不是历史生产库的升级迁移。若已有开发库,先备份并核对数据,再由实际宿主决定重建或编写数据迁移,不要直接把新基线应用到旧库。审查记录见 docs/review.md。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Tai.Wallet.Domain (>= 0.1.8)
- Volo.Abp.EntityFrameworkCore (>= 10.6.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Tai.Wallet.EntityFrameworkCore:
| Package | Downloads |
|---|---|
|
Tai.Wallet.Withdrawal.EntityFrameworkCore
Optional ABP stored-value wallet withdrawal workflow |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.0 | 132 | 11/1/2026 |
| 1.1.0 | 131 | 10/18/2026 |
| 1.0.0 | 169 | 10/17/2026 |
| 0.2.0 | 102 | 10/11/2026 |
| 0.1.9 | 102 | 10/7/2026 |
| 0.1.8 | 105 | 10/5/2026 |
| 0.1.7 | 1,464 | 8/21/2020 |
| 0.1.6 | 653 | 8/20/2020 |
| 0.1.5 | 657 | 8/20/2020 |
| 0.1.4 | 719 | 7/8/2020 |
| 0.1.3 | 663 | 7/8/2020 |
| 0.1.2 | 702 | 7/7/2020 |
| 0.1.1 | 662 | 7/6/2020 |
| 0.1.0 | 648 | 6/29/2020 |