You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中如何抽象Stripe迭代器以复用GetAll函数?

针对Stripe多类型迭代器的通用GetAll函数实现方案

针对你遇到的Stripe多类型迭代器无法复用GetAll逻辑的问题,这里有几种实用的Go语言解决方案:


方案1:接口抽象+适配器模式

先定义通用迭代器接口,把不同类型的Stripe迭代器包装成适配该接口的结构体,实现类型统一。

步骤1:定义通用迭代器接口

// 通用迭代器接口,统一Next和获取当前元素的行为
type StripeIterator interface {
    Next() bool
    Current() interface{}
    Err() error // 统一错误检查方法
}

步骤2:为每个Stripe迭代器写适配器

以Coupon迭代器为例,其他类型(如Customer、Product)同理:

// Coupon迭代器适配器
type CouponIterAdapter struct {
    iter *coupon.Iter
}

func (a *CouponIterAdapter) Next() bool {
    return a.iter.Next()
}

func (a *CouponIterAdapter) Current() interface{} {
    return a.iter.Coupon()
}

func (a *CouponIterAdapter) Err() error {
    return a.iter.Err()
}

步骤3:定义通用迭代器工厂类型和GetAll函数

// 迭代器工厂函数:接收起始ID,返回通用迭代器
type IteratorFactory func(startingAfter string) StripeIterator

// 通用GetAll函数
func GetAll(factory IteratorFactory, processItem func(item interface{}) error) error {
    okToContinue := true
    for okToContinue {
        lastId := ""
        pageLineCount := 0
        iter := factory(lastId)

        for iter.Next() && okToContinue {
            item := iter.Current()
            if err := processItem(item); err != nil {
                return err
            }
            // 提取当前项ID用于下一页分页
            if obj, ok := item.(interface{ GetID() string }); ok {
                lastId = obj.GetID()
            }
            pageLineCount++
        }

        // 检查迭代器错误
        if err := iter.Err(); err != nil {
            return err
        }

        okToContinue = pageLineCount > 0
    }
    return nil
}

步骤4:调用通用函数

// Coupon的迭代器工厂
func couponFactory(startingAfter string) StripeIterator {
    params := &stripe.CouponListParams{}
    params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize)
    if startingAfter != "" {
        params.Filters.AddFilter("starting_after", "", startingAfter)
    }
    return &CouponIterAdapter{iter: coupon.List(params)}
}

// 调用示例
func main() {
    err := GetAll(couponFactory, func(item interface{}) error {
        coupon, ok := item.(*stripe.Coupon)
        if !ok {
            return fmt.Errorf("无效的Coupon类型")
        }
        // 这里写你的数据处理逻辑(如存文件、数据库)
        fmt.Printf("处理Coupon:%s\n", coupon.ID)
        return nil
    })
    if err != nil {
        log.Fatal(err)
    }
}

方案2:Go泛型实现(Go 1.18+)

如果你的项目用Go 1.18及以上版本,泛型可以让代码更简洁,避免重复写适配器:

步骤1:定义泛型迭代器接口

// 泛型迭代器接口,指定元素类型T
type GenericStripeIter[T any] interface {
    Next() bool
    Get() T
    Err() error
}

步骤2:包装Stripe迭代器实现泛型接口

// Coupon泛型迭代器包装器
type CouponGenericIter struct {
    iter *coupon.Iter
}

func (i *CouponGenericIter) Next() bool {
    return i.iter.Next()
}

func (i *CouponGenericIter) Get() *stripe.Coupon {
    return i.iter.Coupon()
}

func (i *CouponGenericIter) Err() error {
    return i.iter.Err()
}

步骤3:泛型版GetAll函数

type GenericIterFactory[T any] func(startingAfter string) GenericStripeIter[T]

func GetAllGeneric[T any](factory GenericIterFactory[T], processItem func(item T) error) error {
    okToContinue := true
    for okToContinue {
        lastId := ""
        pageLineCount := 0
        iter := factory(lastId)

        for iter.Next() && okToContinue {
            item := iter.Get()
            if err := processItem(item); err != nil {
                return err
            }
            if obj, ok := any(item).(interface{ GetID() string }); ok {
                lastId = obj.GetID()
            }
            pageLineCount++
        }

        if err := iter.Err(); err != nil {
            return err
        }

        okToContinue = pageLineCount > 0
    }
    return nil
}

步骤4:调用泛型函数

func couponGenericFactory(startingAfter string) GenericStripeIter[*stripe.Coupon] {
    params := &stripe.CouponListParams{}
    params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize)
    if startingAfter != "" {
        params.Filters.AddFilter("starting_after", "", startingAfter)
    }
    return &CouponGenericIter{iter: coupon.List(params)}
}

// 调用示例
err := GetAllGeneric(couponGenericFactory, func(coupon *stripe.Coupon) error {
    fmt.Printf("处理Coupon:%s\n", coupon.ID)
    return nil
})

方案3:闭包直接封装(最简洁)

不用定义接口和适配器,直接用闭包包装Stripe迭代器的方法,快速实现复用:

// 通用GetAll函数,接收迭代逻辑闭包和处理回调
func GetAllSimple(
    getIter func(startingAfter string) (next func() bool, current func() interface{}, err func() error),
    processItem func(item interface{}) error,
) error {
    okToContinue := true
    for okToContinue {
        lastId := ""
        pageLineCount := 0
        next, current, getErr := getIter(lastId)

        for next() && okToContinue {
            item := current()
            if err := processItem(item); err != nil {
                return err
            }
            if obj, ok := item.(interface{ GetID() string }); ok {
                lastId = obj.GetID()
            }
            pageLineCount++
        }

        if err := getErr(); err != nil {
            return err
        }

        okToContinue = pageLineCount > 0
    }
    return nil
}

调用示例

err := GetAllSimple(
    func(startingAfter string) (func() bool, func() interface{}, func() error) {
        params := &stripe.CouponListParams{}
        params.Filters.AddFilter("limit", "", config.Config.Input.StripeMaxPageSize)
        if startingAfter != "" {
            params.Filters.AddFilter("starting_after", "", startingAfter)
        }
        iter := coupon.List(params)
        return iter.Next, func() interface{} { return iter.Coupon() }, iter.Err
    },
    func(item interface{}) error {
        coupon := item.(*stripe.Coupon)
        fmt.Printf("处理Coupon:%s\n", coupon.ID)
        return nil
    },
)

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 11:05:00