背景

最近研究钉钉机器人的时候,总是会向钉钉官方接口发送很多请求,发送请求的时候,需要封装body结构体传递参数,还要封装response结构体用来接收返回值,有时候,response结构体中还要嵌套一些结构体,这些结构体大多知识在请求的时候能用到,到了其他位置则是没有任何用途。 在声明了一个又一个的结构体后,我发现,这是一个很麻烦的事情,首先是代码很多,显得有点乱,其次是,结构体直接跳转的时候也不是很方便。 然后我们了解了匿名结构体和结构体嵌套,下面来一起看看吧。

具体实现

先看一个没有匿名结构体和结构体嵌套的代码。

//批量获取考勤组
func GetAttendancesGroups(token string, offset int, size int) (groups []dingding.DingAttendGroup, err error) {
    var client *http.Client
    var request *http.Request
    var resp *http.Response
    var body []byte
    URL := "https://oapi.dingtalk.com/topapi/attendance/getsimplegroups?access_token=" + token
    client = &http.Client{Transport: &http.Transport{ //对客户端进行一些配置
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }, Timeout: time.Duration(time.Second * 5)}
    //此处是post请求的请求题,我们先初始化一个对象
    b := BodyGetAttendancesGroups{
        Offset: offset,
        Size:   size,
    }
    //然后把结构体对象序列化一下
    bodymarshal, err := json.Marshal(&b)
    if err != nil {
        return
    }
    //再处理一下
    reqBody := strings.NewReader(string(bodymarshal))
    //然后就可以放入具体的request中的
    request, err = http.NewRequest(http.MethodPost, URL, reqBody)
    if err != nil {
        return
    }
    resp, err = client.Do(request)
    if err != nil {
        return
    }
    defer resp.Body.Close()
    body, err = ioutil.ReadAll(resp.Body) //把请求到的body转化成byte[]
    if err != nil {
        return
    }
    r := ResponseGetAttendancesGroups{}
    //把请求到的结构反序列化到专门接受返回值的对象上面
    err = json.Unmarshal(body, &r)
    if err != nil {
        return
    }
    if r.Errcode != 0 {
        return nil, errors.New("token有误,尝试输入新token")
    }
    // 此处举行具体的逻辑判断,然后返回即可
    r.Result.Groups = groups
    return groups, nil
}
//传递body参数的结构体
type BodyGetAttendancesGroups struct {
    Offset int `json:"offset"`
    Size   int `json:"size"`
}
//接收response返回值的结构体
type ResponseGetAttendancesGroups struct {
    Errcode int                        `json:"errcode"`
    Result  ResultGetAttendancesGroups `json:"result"`
}
type ResultGetAttendancesGroups struct {
    Groups []DingAttendGroup `json:"groups"`
}
type DingAttendGroup struct {
    GroupId       int      `json:"group_id"`      //考勤组id
    GroupName     string   `json:"group_name"`    //考勤组名称
    MemberCount   int      `json:"member_count"`  //参与考勤人员总数
    WorkDayList   []string `json:"work_day_list"` //0表示休息,数组内的值,从左到右依次代表周日到周六,每日的排班情况。
    ClassesList   []string `json:"classes_list"`  // 一周的班次时间展示列表
    SelectedClass []struct {
        Setting struct {
            PermitLateMinutes int `json:"permit_late_minutes"` //允许迟到时长
        } `json:"setting"`
        Sections []struct {
            Times []struct {
                CheckTime string `json:"check_time"` //打卡时间
                CheckType string `json:"check_type"` //打卡类型
            } `json:"times"`
        } `json:"sections"`
    } `json:"selected_class"`
}

上面的代码其实不是我最初的原版代码,可以最后一个结构体,其实我已经使用了嵌套结构体,把很多的结构体,全部写在一起,这样的话,组成一个大大的结构体,代码就不会显得冗余了,所有有关系的结构体都被紧紧的绑定在了一起。上面就是结构体嵌套的好处。

除了结构体嵌套,还有就是匿名结构体,匿名结构体的意思就是,没有具体名称的结构体,用途就是,只使用一次,后续其他地方也用不到了。 这不就完全符合用来接收钉钉官方接口返回的参数,出了这个接口请假内部就再也用不到了。 所有下方代码,就是使用结构体嵌套和匿名结构体后的代码,能够封装一个很强的网络请求,所有需要的结构体全部都封装在一个方法里面。

//此结构体是公用的,需要定义在外面
type DingAttendGroup struct {
    GroupId       int      `json:"group_id"`      //考勤组id
    GroupName     string   `json:"group_name"`    //考勤组名称
    MemberCount   int      `json:"member_count"`  //参与考勤人员总数
    WorkDayList   []string `json:"work_day_list"` //0表示休息,数组内的值,从左到右依次代表周日到周六,每日的排班情况。
    ClassesList   []string `json:"classes_list"`  // 一周的班次时间展示列表
    SelectedClass []struct {
        Setting struct {
            PermitLateMinutes int `json:"permit_late_minutes"` //允许迟到时长
        } `json:"setting"`
        Sections []struct {
            Times []struct {
                CheckTime string `json:"check_time"` //打卡时间
                CheckType string `json:"check_type"` //打卡类型
            } `json:"times"`
        } `json:"sections"`
    } `json:"selected_class"`
}
//此结构体是公用的,需要定义在外面
type DingResponseCommon struct {
    Errcode int `json:"errcode"`
    Errmsg string `json:"errmsg"`
}
//批量获取考勤组
func (a *DingAttendGroup) GetAttendancesGroups(token string, offset int, size int) (groups []DingAttendGroup, err error) {
    var client *http.Client
    var request *http.Request
    var resp *http.Response
    var body []byte
    URL := "https://oapi.dingtalk.com/topapi/attendance/getsimplegroups?access_token=" + token
    client = &http.Client{Transport: &http.Transport{ //对客户端进行一些配置
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }, Timeout: time.Duration(time.Second * 5)}
    //此处是post请求的请求题,我们先初始化一个匿名结构体对象
    b := struct {
        Offset int
        Size   int
    }{
        Offset: offset,
        Size:   size,
    }
    //然后把结构体对象序列化一下
    bodymarshal, err := json.Marshal(&b)
    if err != nil {
        return
    }
    //再处理一下
    reqBody := strings.NewReader(string(bodymarshal))
    //然后就可以放入具体的request中的
    request, err = http.NewRequest(http.MethodPost, URL, reqBody)
    if err != nil {
        return
    }
    resp, err = client.Do(request)
    if err != nil {
        return
    }
    defer resp.Body.Close()
    body, err = ioutil.ReadAll(resp.Body) //把请求到的body转化成byte[]
    if err != nil {
        return
    }
    //初始化一个匿名结构体对象,里面使用了结构体嵌套
    r := struct {
        DingResponseCommon
        Result struct {
            Groups []DingAttendGroup `json:"groups"`
        } `json:"result"`
    }{}
    //把请求到的结构反序列化到专门接受返回值的对象上面
    err = json.Unmarshal(body, &r)
    if err != nil {
        return
    }
    if r.Errcode != 0 {
        return nil, errors.New("token有误,尝试输入新token")
    }
    // 此处举行具体的逻辑判断,然后返回即可
    r.Result.Groups = groups
    return groups, nil
}

好了,这是一个写了很久代码之后,才发现的好方法,希望能帮到你,感谢批评指正。