Files
huang 18daeae65a
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m17s
feat: 钱包系统分离 - 代理钱包与卡钱包完全隔离
## 变更概述
将统一钱包系统拆分为代理钱包和卡钱包两个独立系统,实现数据表和代码层面的完全隔离。

## 数据库变更
- 新增 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>
2026-02-25 09:51:00 +08:00

209 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 钱包系统分离 - 实施任务清单
## 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_noARCH 前缀),状态为"待支付"
- [ ] 16.3 测试代理充值支付完成:模拟支付回调,验证状态从"待支付"变为"已支付"
- [ ] 16.4 测试代理充值到账:处理充值到账,验证钱包余额增加,状态变为"已完成",创建交易记录
- [ ] 16.5 测试卡充值最小金额限制:尝试充值 0.5 元,验证返回"充值金额不能低于 1 元"错误
- [ ] 16.6 测试卡充值订单创建:创建充值订单,验证生成唯一的 recharge_noCRCH 前缀),状态为"待支付"
- [ ] 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. **手动测试不可跳过**:所有核心流程必须手动测试验证,确保功能正确