Tai.Wallet.EntityFrameworkCore 0.1.8

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" Version="0.1.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tai.Wallet.EntityFrameworkCore" Version="0.1.8" />
                    
Directory.Packages.props
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tai.Wallet.EntityFrameworkCore --version 0.1.8
                    
#r "nuget: Tai.Wallet.EntityFrameworkCore, 0.1.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tai.Wallet.EntityFrameworkCore@0.1.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tai.Wallet.EntityFrameworkCore&version=0.1.8
                    
Install as a Cake Addin
#tool nuget:?package=Tai.Wallet.EntityFrameworkCore&version=0.1.8
                    
Install as a Cake Tool

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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