你说你会用反射,我觉得你在吹牛逼

2022/01/20 go
我想让你知道:
1、反射可以将interface类型变量转换称反射对象
2、反射可以将反射对象还原成interface对象
3、反射对象可以修改,value值必须是可设置的

那么什么是反射呢?接下来就让我们开启反射之旅吧

一、什么是反射?

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),
并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。每种语言的反射模型都不同,并且有些语言根本不支持反射。Golang语言实现了反射,
反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。

二、反射的使用场景

1、动态初始化结构体数值

通常我们做业务开发的时候,需要定义表字段的状态值,如果一个个手动设置很麻烦!
那么我们通过反射来动态初始化字段的状态值,做到状态统一管理。比如订单的状态会有多个
case,那么接下来咱们用反射来实现多个状态case初始化

package main

import (
	"fmt"
	"github.com/stoewer/go-strcase"
	"reflect"
)

type orderState struct {
	Closed  string
	WaitPay string
	Paid    string
	Cancel  string
}

var OrderStates orderState

func main() {

	v := reflect.ValueOf(&OrderStates).Elem()
	//订单状态动态初始化
	initStrAttr(v)
	fmt.Println(OrderStates)
	//out {closed wait_pay paid cancel}

}

func initStrAttr(v reflect.Value) {
	for i := 0; i < v.NumField(); i++ {
		t := v.Field(i).Kind()
		name := v.Type().Field(i).Name
		if v.Field(i).Kind() == reflect.String {
			v.Field(i).SetString(strcase.SnakeCase(name))
		} else {
			panic(fmt.Sprintf("结构体字段 %s 类型必须为字符串,而不是%s", name, t))
		}

	}
}

2、数据导出到excel

每次产品让导出不同表数据的时候,总要写一套导出的逻辑,干着吃力不讨好事情,没意思!那么有没有满足万能导出的方案呢? 我说有,可以通过反射来实现!看下面的案例,教你如果写出万能导出数据到excel代码。

package main

import (
	"fmt"
	"github.com/xuri/excelize/v2"
	"math/rand"
	"reflect"
	"time"
)

type DocumentRow struct {
	Id          string `json:"id" h:"章节ID"`          //章节ID
	Name        string `json:"name" h:"标题"`          //标题
	Author      string `json:"author" h:"作者"`        //作者
	Logo        string `json:"logo" h:"封面图片"`        //封面图片
	LogoUrl     string `json:"logo_url" h:"封面地址"`    //封面地址
	Description string `json:"description" h:"文章描述"` //文章描述
	Type        string `json:"type" h:"类型"`          //类型
	TypeValue   string `json:"type_value" h:"类型值"`   //类型值
	Status      string `json:"status" h:"状态"`        //状态
	StatusValue string `json:"status_value" h:"状态值"` //状态值
	CreatorName string `json:"creator_name" h:"创建人"` //创建人
	OnAt        string `json:"on_at" h:"上架时间"`       //上架时间
	Views       int    `json:"views" h:"章节浏览次数"`     //章节浏览次数
	Weight      int    `json:"weight" h:"排序权重"`      //排序权重

}

func getData(num int) []DocumentRow {

	var list = make([]DocumentRow, 0, num)

	for i := 0; i < num; i++ {
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		no := r.Intn(9999999-8888888) + 8888888

		list = append(list, DocumentRow{
			Id:          fmt.Sprintf("%d", i),
			Name:        fmt.Sprintf("文章_%d", no),
			Author:      "张三",
			Logo:        "",
			LogoUrl:     "",
			Description: "",
			Type:        "",
			TypeValue:   "",
			Status:      "",
			StatusValue: "",
			CreatorName: "",
			OnAt:        "",
			Views:       0,
			Weight:      0,
		})

	}

	return list

}

func main() {

	data := getData(100)
	ExportExcel("./document.xlsx", "文章列表", data)

}

// ExportExcel 数据导出到excel
// filePath 保存的路径
// sheetName 工作标名称
// data 要导的数据
func ExportExcel(filePath, sheetName string, data interface{}) {

	v := reflect.ValueOf(data)
	if v.Kind() != reflect.Slice {
		panic("not slice")
	}

	if v.Len() == 0 {
		return
	}

	f := excelize.NewFile()
	err := f.SetSheetName("Sheet1", sheetName)
	if err != nil {
		panic(err)
	}
	t := v.Index(0).Type()
	heads := make([]interface{}, 0, t.NumField())
	for i := 0; i < v.Len(); i++ {
		item := make([]interface{}, 0, t.NumField())
		for j := 0; j < t.NumField(); j++ {
			if i == 0 {
				name := t.Field(j).Tag.Get("h")
				heads = append(heads, name)
			}
			item = append(item, v.Index(i).Field(j))
		}
		if i == 0 {
			if err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", 1), &heads); err != nil {
				panic(err)
			}
		}
		if err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", i+2), &item); err != nil {
			panic(err)
		}
	}

	if err = f.SaveAs(filePath); err != nil {
		panic(err)
	}
}

3、excel导入到数据库

我们经常需要将不同的excel数据导入到不同表中,每次都要写不同导入逻辑,烦都烦死了,那么咱们我怎么写一个通用的 将excel导入逻辑呢?以后接入新的导入需求,直接调用它就可以了,下面咱们就用反射实现它。

package reflects

import (
	"github.com/xuri/excelize/v2"
	"reflect"
	"strings"
)

type Title struct {
	Index      int    //当前行记录第i列索引下标
	Field      string //struct 字段名字
	FieldIndex int    //struct 字段下标
	Name       string //excel 头部标题
}

func ImportDb(filePath string, data any) {

	v := reflect.ValueOf(data).Elem()
	if v.Kind() != reflect.Slice {
		panic("hello")
	}

	t := v.Type().Elem()

	f, err := excelize.OpenFile(filePath)
	if err != nil {
		println(err.Error())
		return
	}
	// 获取 Sheet1 上所有单元格
	rows, err := f.GetRows("Sheet1")
	if len(rows) == 0 {
		panic("暂无数据")
	}

	if t.NumField() != len(rows[0]) {
		panic("表格头字段缺失")
	}

	titles := make([]Title, 0, len(rows[0]))

	for i := 0; i < len(rows[0]); i++ {
		tmp := Title{
			Index: i,
			Field: "",
			Name:  rows[0][i],
		}

		//匹配头部标题
		for j := 0; j < t.NumField(); j++ {
			if strings.Trim(tmp.Name, " ") == strings.Trim(t.Field(j).Tag.Get("h"), " ") {
				tmp.Field = t.Field(j).Name
				tmp.FieldIndex = j
			}
		}
		titles = append(titles, tmp)
	}

	for i := 1; i < len(rows); i++ {
		info := reflect.New(t).Elem()
		for j := 0; j < len(titles); j++ {
			value := ""
			if len(rows[i]) > titles[j].Index {
				value = rows[i][titles[j].Index]
			}

			f := info.Field(titles[j].FieldIndex)
			switch f.Kind() {
			case reflect.String:
				f.SetString(value)
			case reflect.Ptr:

			}
		}
		v.Set(reflect.Append(v, info))
	}
}


type HelpApplyListRow struct {
	Id               string      `json:"id"  h:"ID"`
	Title            string      `json:"title"  h:"援助室名称"`            //援助室名称
	PatientName      string      `json:"patient_name"  h:"被援助人"`       //被援助人
	Content          string      `json:"content"  h:"援助内容"`            //援助内容
	OrganizationId   string      `json:"organization_id"  h:"申请机构id"`  //申请机构id
	OrganizationName string      `json:"organization_name"  h:"申请机构"`  //申请机构
	Status           string      `json:"status"  h:"申请状态"`             //申请状态
	StatusValue      string      `json:"status_value"  h:"申请状态映射值"` //申请状态映射值
	State            string      `json:"state"  h:"援助状态"`              //援助状态
	StateValue       string      `json:"state_value"  h:"援助状态映射值"`  //援助状态映射值
	CreatorName      string      `json:"creator_name"  h:"申请人姓名"`     //申请人姓名
	Reason           string      `json:"reason"  h:"拒绝原因"`             //拒绝原因
	StartAt          *gtime.Time `json:"start_at"  h:"开始时间"`           //开始时间
	EndAt            *gtime.Time `json:"end_at"  h:"结束时间"`             //结束时间
}


func main() {

	//数据导入到excel
	//data := getData(100)
	//reflects.ExportExcel("./document.xlsx", "文章列表", data)
	//
	//data2 := getData2(1000)
	//reflects.ExportExcel("./applyhelp.xlsx", "援助申请", data2)
	
	path, err := os.Getwd()
	if err != nil {
		panic(err)
	}

	var list []HelpApplyListRow

	reflects.ImportDb(path+"/applyhelp.xlsx", &list)
	
}
好了,以上就是我介绍反射的使用场景。

Search

    Table of Contents