feat: 钱包系统分离 - 代理钱包与卡钱包完全隔离
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m17s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m17s
## 变更概述 将统一钱包系统拆分为代理钱包和卡钱包两个独立系统,实现数据表和代码层面的完全隔离。 ## 数据库变更 - 新增 6 张表:tb_agent_wallet、tb_agent_wallet_transaction、tb_agent_recharge_record、tb_card_wallet、tb_card_wallet_transaction、tb_card_recharge_record - 删除 3 张旧表:tb_wallet、tb_wallet_transaction、tb_recharge_record - 代理钱包:按 (shop_id, wallet_type) 唯一标识,支持主钱包和分佣钱包 - 卡钱包:按 (resource_type, resource_id) 唯一标识,支持物联网卡和设备 ## 代码变更 - Model 层:新增 AgentWallet、AgentWalletTransaction、AgentRechargeRecord、CardWallet、CardWalletTransaction、CardRechargeRecord 模型 - Store 层:新增 6 个独立 Store,支持事务、乐观锁、Redis 缓存 - Service 层:重构 commission_calculation、commission_withdrawal、order、recharge 等 8 个服务 - Bootstrap 层:更新 Store 和 Service 依赖注入 - 常量层:按钱包类型重新组织常量和 Redis Key 生成函数 ## 技术特性 - 乐观锁:使用 version 字段防止并发冲突 - 多租户:支持 shop_id_tag 和 enterprise_id_tag 过滤 - 事务管理:所有余额变动使用事务保证 ACID - 缓存策略:Cache-Aside 模式,余额变动后删除缓存 ## 业务影响 - 代理钱包和卡钱包业务完全隔离,互不影响 - 为独立监控、优化、扩展打下基础 - 提升代理钱包的稳定性和独立性 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
# 钱包系统分离 - 实施任务清单
|
||||
|
||||
## 1. 数据库迁移 - 创建新表结构
|
||||
|
||||
- [x] 1.1 创建代理钱包主表迁移文件(`tb_agent_wallet`),包含 shop_id、wallet_type、balance、frozen_balance、version 等字段,添加唯一索引和多租户索引
|
||||
- [x] 1.2 创建代理钱包交易记录表迁移文件(`tb_agent_wallet_transaction`),包含 agent_wallet_id、shop_id、transaction_type、amount、balance_before、balance_after 等字段,添加 4 个查询索引
|
||||
- [x] 1.3 创建代理充值记录表迁移文件(`tb_agent_recharge_record`),包含 recharge_no、amount、payment_method、status 等字段,添加状态和店铺索引
|
||||
- [x] 1.4 创建卡钱包主表迁移文件(`tb_card_wallet`),包含 resource_type、resource_id、balance、frozen_balance、version 等字段,添加唯一索引和多租户索引
|
||||
- [x] 1.5 创建卡钱包交易记录表迁移文件(`tb_card_wallet_transaction`),包含 card_wallet_id、resource_type、resource_id、transaction_type、amount 等字段,添加 4 个查询索引
|
||||
- [x] 1.6 创建卡充值记录表迁移文件(`tb_card_recharge_record`),包含 recharge_no、amount、payment_method、status 等字段,添加状态和资源索引
|
||||
- [x] 1.7 执行数据库迁移,验证 6 张新表创建成功,检查索引和约束
|
||||
|
||||
## 2. 代理钱包系统 - Model 层实现
|
||||
|
||||
- [x] 2.1 创建 `internal/model/agent_wallet.go`,定义 AgentWallet 结构体,包含 ShopID、WalletType、Balance、FrozenBalance、Version、BaseModel 等字段
|
||||
- [x] 2.2 在 `agent_wallet.go` 中定义 AgentWalletTransaction 结构体,包含 AgentWalletID、ShopID、TransactionType、Amount、BalanceBefore、BalanceAfter 等字段
|
||||
- [x] 2.3 在 `agent_wallet.go` 中定义 AgentRechargeRecord 结构体,包含 UserID、AgentWalletID、ShopID、RechargeNo、Amount、PaymentMethod、Status 等字段
|
||||
- [x] 2.4 为所有 Model 实现 TableName() 方法,返回对应的表名(tb_agent_wallet、tb_agent_wallet_transaction、tb_agent_recharge_record)
|
||||
- [x] 2.5 添加中文注释说明每个字段的用途和约束
|
||||
- [x] 2.6 编译验证 Model 定义正确,无语法错误
|
||||
|
||||
## 3. 代理钱包系统 - Store 层实现
|
||||
|
||||
- [x] 3.1 创建 `internal/store/postgres/agent_wallet_store.go`,定义 AgentWalletStore 结构体,包含 db 和 redis 字段
|
||||
- [x] 3.2 实现 `NewAgentWalletStore` 构造函数
|
||||
- [x] 3.3 实现 `GetCommissionWallet(ctx, shopID)` 方法,查询店铺的分佣钱包
|
||||
- [x] 3.4 实现 `GetMainWallet(ctx, shopID)` 方法,查询店铺的主钱包
|
||||
- [x] 3.5 实现 `GetByShopIDAndType(ctx, shopID, walletType)` 方法,根据店铺 ID 和钱包类型查询
|
||||
- [x] 3.6 实现 `GetByID(ctx, id)` 方法,根据钱包 ID 查询
|
||||
- [x] 3.7 实现 `DeductFrozenBalanceWithTx(ctx, tx, walletID, amount)` 方法,从冻结余额扣款(带事务)
|
||||
- [x] 3.8 实现 `UnfreezeBalanceWithTx(ctx, tx, walletID, amount)` 方法,解冻余额到可用余额(带事务)
|
||||
- [x] 3.9 实现 `FreezeBalanceWithTx(ctx, tx, walletID, amount, version)` 方法,冻结余额(带事务,使用乐观锁)
|
||||
- [x] 3.10 实现 `GetShopCommissionSummaryBatch(ctx, shopIDs)` 方法,批量获取店铺佣金钱包汇总
|
||||
- [x] 3.11 创建 `internal/store/postgres/agent_wallet_transaction_store.go`,定义 AgentWalletTransactionStore 结构体
|
||||
- [x] 3.12 实现 `CreateWithTx(ctx, tx, transaction)` 方法,创建代理钱包交易记录(带事务)
|
||||
- [x] 3.13 实现 `ListByShopID(ctx, shopID, offset, limit)` 方法,按店铺查询交易记录(支持分页)
|
||||
- [x] 3.14 创建 `internal/store/postgres/agent_recharge_store.go`,定义 AgentRechargeStore 结构体
|
||||
- [x] 3.15 实现充值记录的 CRUD 方法(Create、GetByRechargeNo、UpdateStatus 等)
|
||||
- [x] 3.16 编译验证 Store 层代码正确,无语法错误
|
||||
|
||||
## 4. 卡钱包系统 - Model 层实现
|
||||
|
||||
- [x] 4.1 创建 `internal/model/card_wallet.go`,定义 CardWallet 结构体,包含 ResourceType、ResourceID、Balance、FrozenBalance、Version、BaseModel 等字段
|
||||
- [x] 4.2 在 `card_wallet.go` 中定义 CardWalletTransaction 结构体,包含 CardWalletID、ResourceType、ResourceID、TransactionType、Amount、BalanceBefore、BalanceAfter 等字段
|
||||
- [x] 4.3 在 `card_wallet.go` 中定义 CardRechargeRecord 结构体,包含 UserID、CardWalletID、ResourceType、ResourceID、RechargeNo、Amount、PaymentMethod、Status 等字段
|
||||
- [x] 4.4 为所有 Model 实现 TableName() 方法,返回对应的表名(tb_card_wallet、tb_card_wallet_transaction、tb_card_recharge_record)
|
||||
- [x] 4.5 添加中文注释说明每个字段的用途和约束
|
||||
- [x] 4.6 编译验证 Model 定义正确,无语法错误
|
||||
|
||||
## 5. 卡钱包系统 - Store 层实现
|
||||
|
||||
- [x] 5.1 创建 `internal/store/postgres/card_wallet_store.go`,定义 CardWalletStore 结构体,包含 db 和 redis 字段
|
||||
- [x] 5.2 实现 `NewCardWalletStore` 构造函数
|
||||
- [x] 5.3 实现 `GetByResourceTypeAndID(ctx, resourceType, resourceID)` 方法,根据资源类型和 ID 查询钱包
|
||||
- [x] 5.4 实现 `GetByID(ctx, id)` 方法,根据钱包 ID 查询
|
||||
- [x] 5.5 实现 `DeductBalanceWithTx(ctx, tx, walletID, amount, version)` 方法,扣款(带事务,使用乐观锁)
|
||||
- [x] 5.6 实现 `AddBalanceWithTx(ctx, tx, walletID, amount)` 方法,增加余额(带事务)
|
||||
- [x] 5.7 创建 `internal/store/postgres/card_wallet_transaction_store.go`,定义 CardWalletTransactionStore 结构体
|
||||
- [x] 5.8 实现 `CreateWithTx(ctx, tx, transaction)` 方法,创建卡钱包交易记录(带事务)
|
||||
- [x] 5.9 实现 `ListByResourceID(ctx, resourceType, resourceID, offset, limit)` 方法,按资源查询交易记录(支持分页)
|
||||
- [x] 5.10 创建 `internal/store/postgres/card_recharge_store.go`,定义 CardRechargeStore 结构体
|
||||
- [x] 5.11 实现充值记录的 CRUD 方法(Create、GetByRechargeNo、UpdateStatus 等)
|
||||
- [x] 5.12 编译验证 Store 层代码正确,无语法错误
|
||||
|
||||
## 6. 常量定义更新
|
||||
|
||||
- [x] 6.1 更新 `pkg/constants/wallet.go`,按钱包类型重新组织常量定义
|
||||
- [x] 6.2 添加代理钱包专用常量(AgentRechargeOrderPrefix、AgentRechargeMinAmount、AgentRechargeMaxAmount)
|
||||
- [x] 6.3 添加卡钱包专用常量(CardWalletResourceTypeIotCard、CardWalletResourceTypeDevice、CardRechargeOrderPrefix、CardRechargeMinAmount、CardRechargeMaxAmount)
|
||||
- [x] 6.4 定义 Redis Key 生成函数:`RedisAgentWalletBalanceKey(shopID, walletType)`
|
||||
- [x] 6.5 定义 Redis Key 生成函数:`RedisAgentWalletLockKey(shopID, walletType)`
|
||||
- [x] 6.6 定义 Redis Key 生成函数:`RedisCardWalletBalanceKey(resourceType, resourceID)`
|
||||
- [x] 6.7 定义 Redis Key 生成函数:`RedisCardWalletLockKey(resourceType, resourceID)`
|
||||
- [x] 6.8 为所有常量和函数添加中文注释
|
||||
- [x] 6.9 编译验证常量定义正确,无语法错误
|
||||
|
||||
## 7. Bootstrap 层 - 注册新 Store
|
||||
|
||||
- [x] 7.1 在 `internal/bootstrap/stores.go` 中添加 AgentWalletStore 字段到 Stores 结构体
|
||||
- [x] 7.2 在 `internal/bootstrap/stores.go` 中添加 AgentWalletTransactionStore 字段到 Stores 结构体
|
||||
- [x] 7.3 在 `internal/bootstrap/stores.go` 中添加 AgentRechargeStore 字段到 Stores 结构体
|
||||
- [x] 7.4 在 `internal/bootstrap/stores.go` 中添加 CardWalletStore 字段到 Stores 结构体
|
||||
- [x] 7.5 在 `internal/bootstrap/stores.go` 中添加 CardWalletTransactionStore 字段到 Stores 结构体
|
||||
- [x] 7.6 在 `internal/bootstrap/stores.go` 中添加 CardRechargeStore 字段到 Stores 结构体
|
||||
- [x] 7.7 在 `NewStores()` 函数中初始化所有新 Store 实例(调用 NewXxxStore 构造函数)
|
||||
- [x] 7.8 编译验证 Bootstrap 层代码正确,无语法错误
|
||||
|
||||
## 8. Service 层重构 - 佣金相关服务
|
||||
|
||||
- [x] 8.1 更新 `internal/service/commission_calculation/service.go`,将 `WalletStore` 依赖改为 `AgentWalletStore`
|
||||
- [x] 8.2 更新 `internal/service/commission_calculation/service.go` 中所有调用钱包的方法,使用新的 AgentWalletStore API
|
||||
- [x] 8.3 更新 `internal/service/commission_withdrawal/service.go`,将 `WalletStore` 依赖改为 `AgentWalletStore`
|
||||
- [x] 8.4 更新 `internal/service/commission_withdrawal/service.go` 中所有调用钱包的方法,使用新的 AgentWalletStore API
|
||||
- [x] 8.5 更新 `internal/service/shop_commission/service.go`,将 `WalletStore` 依赖改为 `AgentWalletStore`
|
||||
- [x] 8.6 更新 `internal/service/shop_commission/service.go` 中所有调用钱包的方法,使用新的 AgentWalletStore API
|
||||
- [x] 8.7 更新 `internal/service/my_commission/service.go`,将 `WalletStore` 依赖改为 `AgentWalletStore`
|
||||
- [x] 8.8 更新 `internal/service/my_commission/service.go` 中所有调用钱包的方法,使用新的 AgentWalletStore API
|
||||
- [x] 8.9 编译验证所有佣金相关服务重构正确,无语法错误
|
||||
|
||||
## 9. Service 层重构 - 订单服务
|
||||
|
||||
- [x] 9.1 更新 `internal/service/order/service.go`,将 `WalletStore` 依赖改为 `AgentWalletStore` 和 `CardWalletStore`
|
||||
- [x] 9.2 更新 `internal/service/order/service.go` 中的 `WalletPay()` 方法,使用新的 AgentWalletStore 和 CardWalletStore API(根据买家类型分别处理)
|
||||
- [x] 9.3 更新 `internal/service/order/service.go` 中的 `HandlePaymentCallback()` 方法,使用新的钱包 API
|
||||
- [x] 9.4 更新 `internal/service/order/service.go` 中所有其他调用钱包的方法,使用新的钱包 Store API
|
||||
- [x] 9.5 编译验证订单服务重构正确,无语法错误
|
||||
|
||||
## 10. Service 层重构 - 充值服务(注:实际采用原地重构方案,未拆分为两个独立服务)
|
||||
|
||||
- [x] 10.1 更新 `internal/service/recharge/service.go`,将依赖从 WalletStore 改为 CardWalletStore、CardWalletTransactionStore、CardRechargeStore
|
||||
- [x] 10.2 更新 Service 构造函数 New(),注入新的 CardWallet 相关 Store
|
||||
- [x] 10.3 更新 `Create()` 方法,使用 CardRechargeStore 创建充值订单,使用 CardWalletStore 查询钱包
|
||||
- [x] 10.4 更新 `HandlePaymentCallback()` 方法,使用 CardRechargeStore 和 CardWalletStore 处理支付回调
|
||||
- [x] 10.5 更新 `buildRechargeResponse()` 方法,适配 CardRechargeRecord 模型
|
||||
- [x] 10.6 更新 `List()` 方法,使用 CardRechargeStore.List() 查询充值记录
|
||||
- [x] 10.7 更新 `GetByID()` 方法,使用 CardRechargeStore.GetByID() 查询充值订单
|
||||
- [x] 10.8 更新所有佣金触发逻辑,使用 AgentWalletStore 处理佣金入账
|
||||
- [x] 10.9 在 CardRechargeStore 中添加 List()、UpdatePaymentInfo()、UpdateStatusWithOptimisticLock() 方法
|
||||
- [x] 10.10 更新 bootstrap/services.go,注入 CardRecharge、CardWallet、CardWalletTransaction Store
|
||||
- [x] 10.11 编译验证充值服务重构正确,无语法错误
|
||||
|
||||
## 11. Bootstrap 层 - 更新 Service 依赖注入
|
||||
|
||||
- [x] 11.1 在 `internal/bootstrap/services.go` 中更新 CommissionCalculationService,注入 AgentWalletStore 和 AgentWalletTransactionStore
|
||||
- [x] 11.2 在 `internal/bootstrap/services.go` 中更新 CommissionWithdrawalService,注入 AgentWalletStore 和 AgentWalletTransactionStore
|
||||
- [x] 11.3 在 `internal/bootstrap/services.go` 中更新 ShopCommissionService,注入 AgentWalletStore
|
||||
- [x] 11.4 在 `internal/bootstrap/services.go` 中更新 MyCommissionService,注入 AgentWalletStore 和 AgentWalletTransactionStore
|
||||
- [x] 11.5 在 `internal/bootstrap/services.go` 中更新 OrderService,注入 AgentWalletStore 和 CardWalletStore
|
||||
- [x] 11.6 在 `internal/bootstrap/services.go` 中更新 RechargeService,注入 CardRechargeStore、CardWalletStore、CardWalletTransactionStore
|
||||
- [x] 11.7 在 `internal/bootstrap/worker_services.go` 中更新 CommissionCalculationService,注入 AgentWalletStore 和 AgentWalletTransactionStore
|
||||
- [x] 11.8 在 `pkg/queue/types.go` 中更新 WorkerStores,添加 AgentWallet 和 AgentWalletTransaction 字段
|
||||
- [x] 11.9 编译验证 Service 依赖注入更新正确,无语法错误
|
||||
|
||||
## 12. 清理旧代码 - 删除旧 Model 和 Store
|
||||
|
||||
- [x] 12.1 删除 `internal/model/wallet.go` 文件(包含 Wallet、WalletTransaction、RechargeRecord、WalletMetadata)
|
||||
- [x] 12.2 删除 `internal/store/postgres/wallet_store.go` 文件
|
||||
- [x] 12.3 删除 `internal/store/postgres/wallet_transaction_store.go` 文件
|
||||
- [x] 12.4 从 `internal/bootstrap/stores.go` 中移除 WalletStore 和 WalletTransactionStore 字段
|
||||
- [x] 12.5 从 `internal/bootstrap/stores.go` 的 `NewStores()` 函数中移除 WalletStore 和 WalletTransactionStore 初始化
|
||||
- [x] 12.6 编译检查,确保无旧代码引用残留
|
||||
|
||||
## 13. 数据库迁移 - 删除旧表
|
||||
|
||||
- [x] 13.1 创建删除旧表的迁移文件(DROP TABLE tb_wallet、tb_wallet_transaction、tb_recharge_record)
|
||||
- [x] 13.2 执行数据库迁移,验证旧表删除成功
|
||||
- [x] 13.3 检查数据库中只剩下新的 6 张表
|
||||
|
||||
## 14. 手动测试 - 代理钱包核心流程
|
||||
|
||||
- [ ] 14.1 测试代理钱包创建:为店铺 ID 10 创建主钱包和分佣钱包
|
||||
- [ ] 14.2 测试代理钱包充值:主钱包充值 100000 分(1000 元),验证余额正确
|
||||
- [ ] 14.3 测试代理钱包扣款:主钱包扣款 30000 分(300 元),验证余额正确,创建交易记录
|
||||
- [ ] 14.4 测试余额不足场景:尝试扣款超过可用余额,验证返回"余额不足"错误
|
||||
- [ ] 14.5 测试冻结余额:分佣钱包冻结 50000 分用于提现,验证 frozen_balance 增加,可用余额减少
|
||||
- [ ] 14.6 测试解冻余额:取消提现,验证冻结余额减少,可用余额恢复
|
||||
- [ ] 14.7 测试并发扣款:模拟两个并发请求同时扣款,验证乐观锁生效(一个成功,一个失败重试)
|
||||
- [ ] 14.8 测试交易记录查询:按店铺 ID 查询交易历史,验证分页和排序正确
|
||||
|
||||
## 15. 手动测试 - 卡钱包核心流程
|
||||
|
||||
- [ ] 15.1 测试卡钱包创建:为物联网卡(resource_type=iot_card, resource_id=100)创建钱包
|
||||
- [ ] 15.2 测试卡钱包充值:卡钱包充值 10000 分(100 元),验证余额正确
|
||||
- [ ] 15.3 测试卡钱包扣款:购买套餐扣款 3000 分(30 元),验证余额正确,创建交易记录
|
||||
- [ ] 15.4 测试订单退款:退款 3000 分,验证余额恢复,创建退款交易记录
|
||||
- [ ] 15.5 测试余额不足场景:尝试扣款超过可用余额,验证返回"余额不足"错误
|
||||
- [ ] 15.6 测试设备钱包:为设备(resource_type=device, resource_id=200)创建钱包,验证设备的多张卡共享钱包
|
||||
- [ ] 15.7 测试并发扣款:模拟两个并发请求同时扣款,验证乐观锁生效
|
||||
- [ ] 15.8 测试交易记录查询:按资源 ID 查询交易历史,验证分页和排序正确
|
||||
|
||||
## 16. 手动测试 - 充值流程
|
||||
|
||||
- [ ] 16.1 测试代理充值最小金额限制:尝试充值 50 元,验证返回"充值金额不能低于 100 元"错误
|
||||
- [ ] 16.2 测试代理充值订单创建:创建充值订单,验证生成唯一的 recharge_no(ARCH 前缀),状态为"待支付"
|
||||
- [ ] 16.3 测试代理充值支付完成:模拟支付回调,验证状态从"待支付"变为"已支付"
|
||||
- [ ] 16.4 测试代理充值到账:处理充值到账,验证钱包余额增加,状态变为"已完成",创建交易记录
|
||||
- [ ] 16.5 测试卡充值最小金额限制:尝试充值 0.5 元,验证返回"充值金额不能低于 1 元"错误
|
||||
- [ ] 16.6 测试卡充值订单创建:创建充值订单,验证生成唯一的 recharge_no(CRCH 前缀),状态为"待支付"
|
||||
- [ ] 16.7 测试卡充值支付完成:模拟支付回调,验证状态从"待支付"变为"已支付"
|
||||
- [ ] 16.8 测试卡充值到账:处理充值到账,验证钱包余额增加,状态变为"已完成",创建交易记录
|
||||
|
||||
## 17. 数据一致性验证
|
||||
|
||||
- [ ] 17.1 验证代理钱包交易记录的 balance_before 和 balance_after 准确性:查询所有交易记录,计算余额变动,与实际余额对比
|
||||
- [ ] 17.2 验证卡钱包交易记录的 balance_before 和 balance_after 准确性:查询所有交易记录,计算余额变动,与实际余额对比
|
||||
- [ ] 17.3 验证乐观锁 version 字段在并发场景下的有效性:模拟高并发扣款,验证 version 正确递增,无丢失更新
|
||||
- [ ] 17.4 验证 Redis 缓存一致性:余额变动后,检查缓存是否被正确删除,下次查询是否重新加载
|
||||
|
||||
## 18. 最终验证和清理
|
||||
|
||||
- [x] 18.1 运行 `go build ./...` 编译整个项目,确保无编译错误
|
||||
- [x] 18.2 运行 `go mod tidy` 清理未使用的依赖
|
||||
- [x] 18.3 检查所有文件的中文注释是否完整
|
||||
- [x] 18.4 检查所有常量是否定义在 `pkg/constants/` 中,无硬编码
|
||||
- [x] 18.5 检查所有错误返回是否使用 `errors.New()` 或 `errors.Wrap()`,无 `fmt.Errorf()`
|
||||
- [x] 18.6 使用 `gofmt -w .` 格式化所有代码
|
||||
- [x] 18.7 检查 git status,确认所有变更符合预期
|
||||
- [x] 18.8 准备提交代码,编写 Git Commit 信息(中文)
|
||||
|
||||
---
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. **任务顺序不可颠倒**:必须先完成数据库迁移和 Model 层,再实现 Store 层,最后重构 Service 层
|
||||
2. **逐项标记完成**:每完成一个任务,将 `[ ]` 改为 `[x]`
|
||||
3. **遇到问题时停止**:如果某个任务无法完成或发现设计问题,立即停止并与团队讨论
|
||||
4. **编译验证**:每个阶段完成后必须编译验证,确保无语法错误
|
||||
5. **手动测试不可跳过**:所有核心流程必须手动测试验证,确保功能正确
|
||||
Reference in New Issue
Block a user