feat: 代理商资金可见性重构(agent-fund-visibility)
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m10s

- 将 GET /shops/commission-summary 重命名为 GET /shops/fund-summary,
  响应新增 main_balance、main_frozen_balance 两个预充值钱包字段
- 新增 GET /shops/:id/main-wallet/transactions 预充值钱包流水接口
- 将佣金统计、每日统计、发起提现从 /my/ 路径迁移至 /shops/:id/ 路径:
  GET /shops/:id/commission-stats
  GET /shops/:id/commission-daily-stats
  POST /shops/:id/withdrawal-requests
- 删除 MyCommissionService、MyCommissionHandler 及全部 /my/ 路由
- 补齐 ListShopWithdrawalRequests、ListShopCommissionRecords 的
  CanManageShop 越权校验(安全修复)
- 提现接口增加严格权限:仅代理账号本人可为自己店铺发起提现

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 14:40:39 +08:00
parent 1d6535b81b
commit 0627ffec42
25 changed files with 1806 additions and 1426 deletions

View File

@@ -233,6 +233,31 @@ func (s *AgentWalletStore) GetShopCommissionSummaryBatch(ctx context.Context, sh
return result, nil
}
// GetShopMainWalletBatch 批量获取店铺主钱包(预充值钱包)
// 返回 map[shopID]*AgentWallet未找到的店铺不会出现在结果中
func (s *AgentWalletStore) GetShopMainWalletBatch(ctx context.Context, shopIDs []uint) (map[uint]*model.AgentWallet, error) {
if len(shopIDs) == 0 {
return make(map[uint]*model.AgentWallet), nil
}
var wallets []model.AgentWallet
query := s.db.WithContext(ctx).
Where("shop_id IN ? AND wallet_type = ?", shopIDs, constants.AgentWalletTypeMain)
// 对齐 GetShopCommissionSummaryBatch 的防御式过滤
query = middleware.ApplyShopFilter(ctx, query)
err := query.Find(&wallets).Error
if err != nil {
return nil, err
}
result := make(map[uint]*model.AgentWallet, len(wallets))
for i := range wallets {
result[wallets[i].ShopID] = &wallets[i]
}
return result, nil
}
// clearWalletCache 清除钱包缓存
func (s *AgentWalletStore) clearWalletCache(ctx context.Context, walletID uint) {
// 查询钱包信息以获取 shop_id 和 wallet_type

View File

@@ -4,7 +4,6 @@ import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
@@ -23,36 +22,39 @@ func NewAgentWalletTransactionStore(db *gorm.DB, redis *redis.Client) *AgentWall
}
}
// AgentWalletTransactionListFilters 主钱包流水过滤条件
type AgentWalletTransactionListFilters struct {
// TransactionType 按交易类型过滤,空字符串表示不过滤
TransactionType string
// StartDate 开始日期,格式 YYYY-MM-DD
StartDate string
// EndDate 结束日期,格式 YYYY-MM-DD
EndDate string
}
// CreateWithTx 创建代理钱包交易记录(带事务)
func (s *AgentWalletTransactionStore) CreateWithTx(ctx context.Context, tx *gorm.DB, transaction *model.AgentWalletTransaction) error {
return tx.WithContext(ctx).Create(transaction).Error
}
// ListByShopID 按店铺查询交易记录(支持分页)
func (s *AgentWalletTransactionStore) ListByShopID(ctx context.Context, shopID uint, offset, limit int) ([]*model.AgentWalletTransaction, error) {
// ListByWalletIDWithFilters 按钱包ID查询交易记录(支持分页和过滤
// 按 created_at DESC 排序
func (s *AgentWalletTransactionStore) ListByWalletIDWithFilters(ctx context.Context, walletID uint, offset, limit int, filters *AgentWalletTransactionListFilters) ([]*model.AgentWalletTransaction, error) {
var transactions []*model.AgentWalletTransaction
query := s.db.WithContext(ctx).
Where("shop_id = ?", shopID)
// 应用数据权限过滤
query = middleware.ApplyShopFilter(ctx, query)
err := query.Order("created_at DESC").
Offset(offset).
Limit(limit).
Find(&transactions).Error
query := s.db.WithContext(ctx).Where("agent_wallet_id = ?", walletID)
query = applyWalletTransactionFilters(query, filters)
err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&transactions).Error
if err != nil {
return nil, err
}
return transactions, nil
}
// CountByShopID 统计店铺的交易记录数量
func (s *AgentWalletTransactionStore) CountByShopID(ctx context.Context, shopID uint) (int64, error) {
// CountByWalletID 统计指定钱包的交易记录数量(支持过滤)
func (s *AgentWalletTransactionStore) CountByWalletID(ctx context.Context, walletID uint, filters *AgentWalletTransactionListFilters) (int64, error) {
var count int64
query := s.db.WithContext(ctx).
Model(&model.AgentWalletTransaction{}).
Where("shop_id = ?", shopID)
// 应用数据权限过滤
query = middleware.ApplyShopFilter(ctx, query)
query := s.db.WithContext(ctx).Model(&model.AgentWalletTransaction{}).Where("agent_wallet_id = ?", walletID)
query = applyWalletTransactionFilters(query, filters)
err := query.Count(&count).Error
return count, err
}
@@ -60,14 +62,8 @@ func (s *AgentWalletTransactionStore) CountByShopID(ctx context.Context, shopID
// ListByWalletID 按钱包查询交易记录(支持分页)
func (s *AgentWalletTransactionStore) ListByWalletID(ctx context.Context, walletID uint, offset, limit int) ([]*model.AgentWalletTransaction, error) {
var transactions []*model.AgentWalletTransaction
query := s.db.WithContext(ctx).
Where("agent_wallet_id = ?", walletID)
// 应用数据权限过滤
query = middleware.ApplyShopFilter(ctx, query)
err := query.Order("created_at DESC").
Offset(offset).
Limit(limit).
Find(&transactions).Error
query := s.db.WithContext(ctx).Where("agent_wallet_id = ?", walletID)
err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&transactions).Error
if err != nil {
return nil, err
}
@@ -77,13 +73,27 @@ func (s *AgentWalletTransactionStore) ListByWalletID(ctx context.Context, wallet
// GetByReference 根据关联业务查询交易记录
func (s *AgentWalletTransactionStore) GetByReference(ctx context.Context, referenceType string, referenceID uint) (*model.AgentWalletTransaction, error) {
var transaction model.AgentWalletTransaction
query := s.db.WithContext(ctx).
Where("reference_type = ? AND reference_id = ?", referenceType, referenceID)
// 应用数据权限过滤
query = middleware.ApplyShopFilter(ctx, query)
query := s.db.WithContext(ctx).Where("reference_type = ? AND reference_id = ?", referenceType, referenceID)
err := query.First(&transaction).Error
if err != nil {
return nil, err
}
return &transaction, nil
}
// applyWalletTransactionFilters 应用过滤条件到查询
func applyWalletTransactionFilters(query *gorm.DB, filters *AgentWalletTransactionListFilters) *gorm.DB {
if filters == nil {
return query
}
if filters.TransactionType != "" {
query = query.Where("transaction_type = ?", filters.TransactionType)
}
if filters.StartDate != "" {
query = query.Where("created_at >= ?", filters.StartDate+" 00:00:00")
}
if filters.EndDate != "" {
query = query.Where("created_at <= ?", filters.EndDate+" 23:59:59")
}
return query
}