Tai.Wallet.EntityFrameworkCore
1.0.0
See the version list below for details.
dotnet add package Tai.Wallet.EntityFrameworkCore --version 1.0.0
NuGet\Install-Package Tai.Wallet.EntityFrameworkCore -Version 1.0.0
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" Version="1.0.0" />
<PackageVersion Include="Tai.Wallet.EntityFrameworkCore" Version="1.0.0" />
<PackageReference Include="Tai.Wallet.EntityFrameworkCore" />
paket add Tai.Wallet.EntityFrameworkCore --version 1.0.0
#r "nuget: Tai.Wallet.EntityFrameworkCore, 1.0.0"
#:package Tai.Wallet.EntityFrameworkCore@1.0.0
#addin nuget:?package=Tai.Wallet.EntityFrameworkCore&version=1.0.0
#tool nuget:?package=Tai.Wallet.EntityFrameworkCore&version=1.0.0
Tai.Wallet
业务储值钱包 1.0.0,基于 .NET 10 / ABP 10.7.0 / EF Core。支持按用户或租户、币种开户,余额与流水查询、入账、消费、部分退款、冻结与结算。不包含支付渠道和出款渠道;不提供 MongoDB 实现。
本次是破坏性重构。旧 Balance / BalanceTransaction 契约和开发期迁移已移除;示例迁移是新数据库基线,不可直接应用到旧结构。若保留开发数据,应先备份,再另行编写转换脚本,不能用删库方式处理已有业务数据。
资金与交易
| 资金 | 用途 | 可提现 |
|---|---|---|
| Cash | 可退回现金的本金 | 是 |
| StoredValue | 兑换码等受限本金 | 否 |
| Bonus | 赠送金 | 否 |
一个账户属于一个租户范围、所有者类型、所有者 ID 和币种。开户显式执行,查询不存在的账户返回 404。金额为最小货币单位的正整数 long,账户已记账总额不超过 long.MaxValue;HTTP 金额和流水序号输出 JSON 字符串,避免 JavaScript 精度损失。核心只校验三位大写币种代码,不执行汇率换算。
五张表为 TaiWalletAccounts、TaiWalletFundBalances、TaiWalletTransactions、TaiWalletEntries、TaiWalletHolds。账户保存并发版本与递增流水序号;资金余额保存 PostedAmount 和 HeldAmount,可用额为二者之差。数据库约束保证 PostedAmount >= HeldAmount >= 0。一笔交易可以含多条资金分录,因此一次充值的本金、赠金共同提交。
Transaction 和 Entry 入账后不可修改、删除;EF 拦截器也保护组合宿主的正常 SaveChanges 写入路径。这不是对数据库管理员或绕过 EF 的 SQL 提供防篡改能力。退款追加交易和分录,关联原交易及原分录,按剩余可退金额分配,手续费不随消费退款自动退回。没有支付渠道退款用例。
默认消费按照可用本金和赠金比例分摊,本金内先使用 StoredValue,再使用 Cash;可通过 ABP Setting Wallet.DebitOrder 选择 Proportional、PrincipalFirst、BonusFirst。消费分摊前先预留手续费,手续费单独从 Cash 扣除。DebitBlocked 禁止新消费、新冻结,允许充值、退款和既有冻结的结算或释放。
安装与宿主
生产包按层独立安装:Tai.Wallet.Domain.Shared、Domain、Application.Contracts、Application、EntityFrameworkCore、HttpApi、HttpApi.Client。宿主通过对应 TaiWallet*Module 声明 DependsOn;数据库宿主实现 IWalletDbContext 的 Accounts、FundBalances、Transactions、Entries、Holds,调用 builder.ConfigureWallet(),通过 AddAbpDbContext 的 ReplaceDbContext<IWalletDbContext>() 接入仓储。参照 samples/Tai.Wallet.Sample 的两个数据库宿主上下文;连接字符串名为 TaiWallet,驱动和实际迁移由宿主负责。
同一钱包写入的账户余额、交易、分录和冻结状态必须在同一个数据库、同一个事务型 ABP Unit of Work 提交。应用集成层也应共享宿主 DbContext,以便业务记录与钱包写入原子提交;不可仅凭相同连接字符串就假定跨 DbContext 共享事务。并发或唯一键冲突后丢弃当前工作单元,在全新工作单元重试相同请求;不要在失败事务内继续写入。
进程内可信调用使用 WalletManager;普通用户 HTTP 不暴露任意充值、扣款能力。例子(执行于事务型 UOW 内):
var account = await wallet.OpenAsync(WalletOwnerType.User, userId, "CNY");
var transaction = await wallet.CreditAsync(account.Id,
new WalletRequest("recharge:business-id", "Recharge", "business-id",
ReasonCode: "MemberTopUp", Description: "会员充值",
Metadata: new Dictionary<string, string> { ["OrderId"] = "order-123" }),
new Dictionary<WalletFund, long> { [WalletFund.Cash] = 10000, [WalletFund.Bonus] = 1000 });
IdempotencyKey 区分大小写,唯一范围为租户、账户、操作。同键同参数返回原结果,同键异参数抛 RequestConflict。数据库唯一索引使用键的 SHA-256,避免 MySQL 默认排序规则忽略大小写。业务 ID、幂等键、交易 ID 分别承担不同职责;金额、关联 ID 和 metadata 都参与请求指纹,服务端消费策略保存在首次交易快照中。
SourceType / SourceId 用于业务来源和过滤;ReasonCode / Description 用于原因与展示。Metadata 为字符串字典,最多 32 项,键最长 64、值最长 1024,按默认 System.Text.Json 编码后的 UTF-8 JSON 不超过 16 KiB。不存凭据或秘密,不以任意 metadata 代替权限、金额、币种等结构化规则。退款未传 metadata 时继承原交易内容。
HTTP 与客户端
| 方法 | 路径 | 行为 |
|---|---|---|
| POST | /api/wallet/accounts | 为当前用户开户,输入 currency |
| GET | /api/wallet/accounts/me?currency=CNY | 当前用户余额 |
| GET | /api/wallet/accounts/{id} | 指定账户余额 |
| GET | /api/wallet/transactions/{id} | 交易详情及分录、metadata |
| GET | /api/wallet/accounts/{id}/transactions | 账户交易,支持 beforeSequence、limit、sourceType、sourceId |
读取需要 Wallet.Accounts.Read,开户另需 Wallet.Accounts.Open;同时检查当前租户和账户归属。分页按序号倒序,下一页使用返回的 NextBeforeSequence。HttpApi.Client 提供 IWalletAccountAppService、IWalletTransactionAppService,RemoteServices 配置键 Wallet。
冻结分 Reserve、Capture、Release:Reserve 仅占用可用余额,Capture 扣减已冻结资金,Release 释放占用;同一冻结只能成功结算或释放一次。未知外部出款结果不可释放,具体流程由 wallet-withdrawal 管理。
ReconcileAsync 用分录汇总核对余额,用于新工作单元中的巡检;并发写入期间多次查询可能跨越提交边界,持续差异应在稳定读视图或停写窗口重新确认,方法不自动修改金额。
验证
bash scripts/verify.sh
# 从仓库根目录按本地 NuGet 包依赖顺序验证全系列:
bash scripts/verify-wallet-series.sh
模块脚本在模块目录解析路径。示例支持 PostgreSQL / MySQL,两套迁移分别检查;WALLET_TEST_PROVIDER=postgres 或 mysql 配合 WALLET_TEST_CONNECTION 可执行 scripts/verify-relational.sh,连接应指向专用测试实例,需要创建和删除随机测试数据库的权限。SQLite 不执行三项真实关系数据库并发测试。HTTP 示例使用经过验证的 JWT,生产权限授予由消费宿主配置。
可选模块:wallet-recharge 组合 Tai.Payment;wallet-voucher 管理兑换码与充值券;wallet-withdrawal 管理提现工作流。详见各自 README 和 术语。
| 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 (>= 1.0.0)
- Volo.Abp.EntityFrameworkCore (>= 10.7.0)
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 |