123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- package exchange
- import (
- "reflect"
- "github.com/spf13/cast"
- "gorm.io/gorm"
- )
- type Exporter struct {
- resource interface{}
- rtResource reflect.Type
- metas []*Meta
- pkMetas []*Meta
- associations []string
- }
- func NewExporter(resource interface{}) *Exporter {
- return &Exporter{
- resource: resource,
- }
- }
- func (ep *Exporter) Metas(ms ...*Meta) *Exporter {
- ep.metas = ms
- return ep
- }
- func (ep *Exporter) Associations(ts ...string) *Exporter {
- ep.associations = ts
- return ep
- }
- func (ep *Exporter) Exec(db *gorm.DB, w Writer, opts ...ExporterExecOption) error {
- err := ep.validateAndInit()
- if err != nil {
- return err
- }
- maxParamsPerSQL := ep.parseOptions(opts...)
- records := reflect.New(reflect.SliceOf(ep.rtResource)).Elem()
- {
- // gorm using id to order in FindInBatches
- // var orderBy string
- // for i, m := range ep.pkMetas {
- // if i > 0 {
- // orderBy += ", "
- // }
- // orderBy += fmt.Sprintf("%s asc", m.snakeField)
- // }
- chunkRecords := reflect.New(reflect.SliceOf(ep.rtResource)).Interface()
- batchSize := maxParamsPerSQL
- if len(ep.pkMetas) > 0 {
- batchSize /= len(ep.pkMetas)
- }
- err = preloadDB(db, ep.associations).
- Model(ep.resource).
- // Order(orderBy).
- FindInBatches(chunkRecords, batchSize, func(tx *gorm.DB, batch int) error {
- records = reflect.AppendSlice(records, reflect.ValueOf(chunkRecords).Elem())
- return nil
- }).Error
- if err != nil {
- return err
- }
- }
- headers := make([]string, 0, len(ep.metas))
- for _, m := range ep.metas {
- headers = append(headers, m.columnHeader)
- }
- err = w.WriteHeader(headers)
- if err != nil {
- return err
- }
- vals := make([]string, len(ep.metas))
- for i := 0; i < records.Len(); i++ {
- record := records.Index(i)
- for i, m := range ep.metas {
- if m.valuer != nil {
- v, err := m.valuer(record.Interface())
- if err != nil {
- return err
- }
- vals[i] = v
- continue
- }
- vals[i] = cast.ToString(record.Elem().FieldByName(m.field).Interface())
- }
- err = w.WriteRow(vals)
- if err != nil {
- return err
- }
- }
- return w.Flush()
- }
- func (ep *Exporter) validateAndInit() error {
- if err := validateResourceAndMetas(ep.resource, ep.metas); err != nil {
- return err
- }
- ep.pkMetas = []*Meta{}
- for i, _ := range ep.metas {
- m := ep.metas[i]
- if m.primaryKey {
- ep.pkMetas = append(ep.pkMetas, m)
- }
- }
- ep.rtResource = reflect.TypeOf(ep.resource)
- return nil
- }
- func (ep *Exporter) parseOptions(opts ...ExporterExecOption) (
- maxParamsPerSQL int,
- ) {
- maxParamsPerSQL = 65000
- for _, opt := range opts {
- switch v := opt.(type) {
- case *maxParamsPerSQLOption:
- maxParamsPerSQL = v.v
- }
- }
- return maxParamsPerSQL
- }
|