diff --git a/api/version1/taskplatform/taskmanagement/importFormFile.go b/api/version1/taskplatform/taskmanagement/importFormFile.go new file mode 100644 index 0000000..bd14e25 --- /dev/null +++ b/api/version1/taskplatform/taskmanagement/importFormFile.go @@ -0,0 +1,189 @@ +package taskmanagement + +import ( + "appPlatform/api/version1/customerform" + "appPlatform/models/customerForm" + "appPlatform/models/modelAppPlatform" + "appPlatform/models/modelshr" + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/csv" + "errors" + "fmt" + "mime/multipart" + "net/http" + "regexp" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/xuri/excelize/v2" +) + +func (a *ApiMethod) ImportFormListFile(ctx *gin.Context) { + formId := ctx.Request.FormValue("formId") + pFileHeader, err := ctx.FormFile("file") + if err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + + file, err := pFileHeader.Open() + if err != nil { + ctx.JSON(http.StatusBadRequest, err) + return + } + defer file.Close() + + //获取form的view,以便可以获取具体的table名 + var formCont modelAppPlatform.CustomerFormView + err = formCont.GetCont(map[string]interface{}{"`id`": formId}) + if err != nil || formCont.TableKey == "" { + ctx.JSON(http.StatusBadGateway, err) + return + } + + //直接读取数据库获取表的详细结构 + masterFieldAry, mastErr := customerform.ReadDatabaseForm(formCont.TableKey) + if mastErr != nil { + ctx.JSON(http.StatusBadGateway, mastErr) + return + } + + isCSV := strings.HasSuffix(pFileHeader.Filename, ".csv") + var ( + title []string + data [][]interface{} + ) + //支持处理excel和csv格式文件 + title, data, err = handleCSVDataFile(file, masterFieldAry, isCSV) + + //构建task记录,每条导入记录绑定一个task + context, _ := ctx.Get(overall.MyContJwt) + var userCont modelshr.ManCont + userCont.GetLoginCont(context) //当前操作人 + cureeTime := time.Now().Unix() //写入时间 + + valueMap := make(map[string]interface{}) + valueMap["creater_time"] = cureeTime + valueMap["creater"] = userCont.Key + valueMap["createrOrg"] = userCont.AdminOrg + valueMap["edit_time"] = cureeTime + i, l := 0, len(title) + for k := range data { + if len(data[k]) != l { + continue + } + _uuid := publicmethod.GetUUid(1) //统一识别符 + valueMap["masters_key"] = _uuid + for ; i < l; i++ { + //准备数据 + valueMap[title[i]] = data[k][i] + } + //重置循环 + i = 0 + delete(valueMap, "@id") + //开始插入 + if err = overall.CONSTANT_DB_CustomerForm.Table(formCont.TableKey).Create(valueMap).Error; err != nil { + break + } + + var taskCont customerForm.TaskRecord + taskCont.MastersKey = _uuid + taskCont.Title = fmt.Sprintf("%v-%v(%v)-%v", formCont.Name, userCont.Name, userCont.Number, publicmethod.UnixTimeToDay(cureeTime, 14)) //标题"` + taskCont.AppKey = formCont.Groupid + taskCont.TableKey = formCont.SignCode + taskCont.Creater = valueMap["creater"].(int64) //创建人"` + taskCont.CreaterTime = valueMap["edit_time"].(int64) //创建时间"` + taskCont.EditTime = valueMap["edit_time"].(int64) //编辑时间"` + taskCont.Types = formCont.FlowIsOpen //类型(1:流程表单;2:普通表单)"` + taskCont.VersionId = formCont.Id //来源于哪个表单"` + taskCont.Status = 2 + taskCont.CreaterOrg = valueMap["createrOrg"].(int64) + taskCont.MastesForm = formCont.MastesForm + taskCont.MastesFormJson = formCont.MastesFormJson + if err = overall.CONSTANT_DB_CustomerForm.Create(&taskCont).Error; err != nil { + break + } + } + if err != nil { + ctx.JSON(http.StatusBadGateway, err) + return + } + ctx.JSON(http.StatusOK, nil) +} + +// 处理excel和csv格式文件 +func handleCSVDataFile(file multipart.File, fields []customerform.Result, iscsv bool) (title []string, result [][]interface{}, err error) { + var records [][]string + if iscsv { + //handle csv file + f := csv.NewReader(file) + records, err = f.ReadAll() + } else { + //handle excel file + fe, err0 := excelize.OpenReader(file) + records, err = fe.GetRows("Sheet1") + if err0 != nil { + err = errors.Join(err) + } + } + + if err != nil { + return + } + + title = records[0] + reg := regexp.MustCompile(`(\w+)`) + + reflects := []string{} + //根据title,获取对应列的具体存储类型 + for k, v := range title { + //(shen1gao1)标题 => shen1gao1 + v = reg.FindString(v) + title[k] = v + for _, f := range fields { + if v == f.Field { + if strings.Contains(f.Type, "varchar") { + reflects = append(reflects, "string") + } else if strings.Contains(f.Type, "int") { + reflects = append(reflects, "int") + } else if strings.Contains(f.Type, "double") || strings.Contains(f.Type, "float") { + reflects = append(reflects, "number") + } else { + reflects = append(reflects, "string") + } + } + } + } + max := len(records) + result = make([][]interface{}, len(title)-1) //丢掉标题行 + for i := 1; i < max; i++ { + arr := []interface{}{} + for k := range records[i] { //csv的第i行数据 + //根据列数,先转换类型,然后在插入到对应的数组中 + switch reflects[k] { + case "int": + vi, err0 := strconv.Atoi(records[i][k]) + if err0 != nil { + err = err0 + return + } + arr = append(arr, vi) + case "number": + vf, err0 := strconv.ParseFloat(records[i][k], 64) + if err0 != nil { + err = err0 + return + } + arr = append(arr, vf) + default: + arr = append(arr, records[i][k]) + } + } + result[i-1] = arr + } + + return +} diff --git a/apirouter/v1/taskrouter/taskrouter.go b/apirouter/v1/taskrouter/taskrouter.go index a065889..c612154 100644 --- a/apirouter/v1/taskrouter/taskrouter.go +++ b/apirouter/v1/taskrouter/taskrouter.go @@ -27,6 +27,8 @@ func (a *ApiRouter) RouterGroupPc(router *gin.RouterGroup) { apiRouter.POST("add_form_newdata", taskManagementRouter.AddFormAddNewData) //用户端自定义表单新增记录(新版) apiRouter.POST("add_customer_form", taskManagementRouter.AddCustomerForm) //用户端自定义表单新增记录(列表新版) + apiRouter.POST("import_form_list", taskManagementRouter.ImportFormListFile) //用户端自定义表单新增记录(列表新版) + apiRouter.POST("gainTaskFormInfo", taskManagementRouter.GainTaskFormInfo) //获取任务表单内容 apiRouter.POST("softDeletion", taskManagementRouter.SoftDeletion) //删除任务数据 diff --git a/go.mod b/go.mod index 463ed41..b1ed0c7 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.16.0 github.com/tjfoc/gmsm v1.4.1 + github.com/xuri/excelize/v2 v2.9.1 gopkg.in/ini.v1 v1.67.0 gorm.io/driver/mysql v1.5.2 gorm.io/gorm v1.25.10 @@ -29,7 +30,12 @@ require ( github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/microsoft/go-mssqldb v1.7.2 // indirect - golang.org/x/sync v0.5.0 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect + github.com/tiendc/go-deepcopy v1.6.0 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/nfp v0.0.1 // indirect + golang.org/x/sync v0.14.0 // indirect ) require ( @@ -64,10 +70,10 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/postgres v1.5.11 diff --git a/go.sum b/go.sum index 60a6d2b..01f9636 100644 --- a/go.sum +++ b/go.sum @@ -168,6 +168,11 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -195,16 +200,25 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tiendc/go-deepcopy v1.6.0 h1:0UtfV/imoCwlLxVsyfUd4hNHnB3drXsfle+wzSCA5Wo= +github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.9.1 h1:VdSGk+rraGmgLHGFaGG9/9IWu1nj4ufjJ7uwMDtj8Qw= +github.com/xuri/excelize/v2 v2.9.1/go.mod h1:x7L6pKz2dvo9ejrRuD8Lnl98z4JLt0TGAwjhW+EiP8s= +github.com/xuri/nfp v0.0.1 h1:MDamSGatIvp8uOmDP8FnmjuQpu90NzdJxo7242ANR9Q= +github.com/xuri/nfp v0.0.1/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= @@ -219,9 +233,12 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -245,16 +262,17 @@ golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -274,8 +292,9 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -295,8 +314,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=