# 场景示例

飞书开放平台基于 SDK，封装了常用的 API 组合调用及业务场景示例供你参考。在 [oapi-sdk-go-demo](https://github.com/larksuite/oapi-sdk-go-demo) 中包含了以下多种场景示例代码。

## [发送文件消息](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/im/send_file.go)

发送文件消息，使用到两个OpenAPI：[上传文件](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create)
和[发送消息](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create)。

```java
package im

import (
        "context"
        "encoding/json"
        "fmt"
        "io"

"github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
        "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)

type SendFileRequest struct {
        FileType      string
        FileName      string
        File          io.Reader
        Duration      int
        ReceiveIdType string
        ReceiveId     string
        Uuid          string
}

type SendFileResponse struct {
        *larkcore.CodeError
        CreateFileResponse    *larkim.CreateFileRespData
        CreateMessageResponse *larkim.CreateMessageRespData
}

// SendFile 发送文件消息
func SendFile(client *lark.Client, request *SendFileRequest) (*SendFileResponse, error) {
        // 上传文件
        createFileReq := larkim.NewCreateFileReqBuilder().
                Body(larkim.NewCreateFileReqBodyBuilder().
                        FileType(request.FileType).
                        FileName(request.FileName).
                        Duration(request.Duration).
                        File(request.File).
                        Build()).
                Build()
        createFileResp, err := client.Im.File.Create(context.Background(), createFileReq)
        if err != nil {
                return nil, err
        }
        if !createFileResp.Success() {
                fmt.Printf("client.Im.File.Create failed, code: %d, msg: %s, log_id: %s\n",
                        createFileResp.Code, createFileResp.Msg, createFileResp.RequestId())
                return nil, createFileResp.CodeError
        }

// 发送消息
        bs, err := json.Marshal(createFileResp.Data)
        if err != nil {
                return nil, err
        }
        createMessageReq := larkim.NewCreateMessageReqBuilder().
                ReceiveIdType(request.ReceiveIdType).
                Body(larkim.NewCreateMessageReqBodyBuilder().
                        ReceiveId(request.ReceiveId).
                        MsgType("file").
                        Content(string(bs)).
                        Uuid(request.Uuid).
                        Build()).
                Build()

createMessageResp, err := client.Im.Message.Create(context.Background(), createMessageReq)
        if err != nil {
                return nil, err
        }
        if !createMessageResp.Success() {
                fmt.Printf("client.Im.Message.Create failed, code: %d, msg: %s, log_id: %s\n",
                        createMessageResp.Code, createMessageResp.Msg, createMessageResp.RequestId())
                return nil, createMessageResp.CodeError
        }

// 返回结果
        return &SendFileResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                CreateFileResponse:    createFileResp.Data,
                CreateMessageResponse: createMessageResp.Data,
        }, nil
}
```

## [发送图片消息](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/im/send_image.go)

发送图片消息，使用到两个OpenAPI：[上传图片](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/image/create)和[发送消息](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create)。

```java
package im

import (
        "context"
        "encoding/json"
        "fmt"
        "io"

"github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
        "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)

type SendImageRequest struct {
        Image         io.Reader
        ReceiveIdType string
        ReceiveId     string
        Uuid          string
}

type SendImageResponse struct {
        *larkcore.CodeError
        CreateImageResponse   *larkim.CreateImageRespData
        CreateMessageResponse *larkim.CreateMessageRespData
}

// SendImage 发送图片消息
func SendImage(client *lark.Client, request *SendImageRequest) (*SendImageResponse, error) {
        // 上传图片
        createImageReq := larkim.NewCreateImageReqBuilder().
                Body(larkim.NewCreateImageReqBodyBuilder().
                        ImageType("message").
                        Image(request.Image).
                        Build()).
                Build()
        createImageResp, err := client.Im.Image.Create(context.Background(), createImageReq)
        if err != nil {
                return nil, err
        }
        if !createImageResp.Success() {
                fmt.Printf("client.Im.Image.Create failed, code: %d, msg: %s, log_id: %s\n",
                        createImageResp.Code, createImageResp.Msg, createImageResp.RequestId())
                return nil, createImageResp.CodeError
        }

// 发送消息
        bs, err := json.Marshal(createImageResp.Data)
        if err != nil {
                return nil, err
        }
        createMessageReq := larkim.NewCreateMessageReqBuilder().
                ReceiveIdType(request.ReceiveIdType).
                Body(larkim.NewCreateMessageReqBodyBuilder().
                        ReceiveId(request.ReceiveId).
                        MsgType("image").
                        Content(string(bs)).
                        Uuid(request.Uuid).
                        Build()).
                Build()

createMessageResp, err := client.Im.Message.Create(context.Background(), createMessageReq)
        if err != nil {
                return nil, err
        }
        if !createMessageResp.Success() {
                fmt.Printf("client.Im.Message.Create failed, code: %d, msg: %s, log_id: %s\n",
                        createMessageResp.Code, createMessageResp.Msg, createMessageResp.RequestId())
                return nil, createMessageResp.CodeError
        }

// 返回结果
        return &SendImageResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                CreateImageResponse:   createImageResp.Data,
                CreateMessageResponse: createMessageResp.Data,
        }, nil
}
```

## [获取部门下所有用户列表](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/contact/list_user_by_department.go)

获取部门下所有用户列表，使用到两个OpenAPI：[获取子部门列表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/contact-v3/department/children)和[获取部门直属用户列表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/contact-v3/user/find_by_department)。

```java
package contact

import (
        "context"
        "fmt"

lark "github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
        larkcontact "github.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
)

type ListUserByDepartmentRequest struct {
        DepartmentId string
}

type ListUserByDepartmentResponse struct {
        *larkcore.CodeError
        ChildrenDepartmentResponse   *larkcontact.ChildrenDepartmentRespData
        FindByDepartmentUserResponse []*larkcontact.User
}

// ListUserByDepartment 获取部门下所有用户列表
func ListUserByDepartment(client *lark.Client, request *ListUserByDepartmentRequest) (*ListUserByDepartmentResponse, error) {
        // 获取子部门列表
        childrenDepartmentReq := larkcontact.NewChildrenDepartmentReqBuilder().
                DepartmentIdType("open_department_id").
                DepartmentId(request.DepartmentId).
                Build()

childrenDepartmentResp, err := client.Contact.Department.Children(context.Background(), childrenDepartmentReq)

if err != nil {
                return nil, err
        }
        if !childrenDepartmentResp.Success() {
                fmt.Printf("client.Contact.Department.Children failed, code: %d, msg: %s, log_id: %s\n",
                        childrenDepartmentResp.Code, childrenDepartmentResp.Msg, childrenDepartmentResp.RequestId())
                return nil, childrenDepartmentResp.CodeError
        }

// 获取部门直属用户列表
        users := make([]*larkcontact.User, 0)
        openDepartmentIds := []string{request.DepartmentId}
        for _, item := range childrenDepartmentResp.Data.Items {
                openDepartmentIds = append(openDepartmentIds, *item.OpenDepartmentId)
        }

for _, id := range openDepartmentIds {
                findByDepartmentUserReq := larkcontact.NewFindByDepartmentUserReqBuilder().
                        DepartmentId(id).
                        Build()

findByDepartmentUserResp, err := client.Contact.User.FindByDepartment(context.Background(), findByDepartmentUserReq)

if err != nil {
                        return nil, err
                }
                if !findByDepartmentUserResp.Success() {
                        fmt.Printf("client.Contact.User.FindByDepartment failed, code: %d, msg: %s, log_id: %s\n",
                                findByDepartmentUserResp.Code, findByDepartmentUserResp.Msg, findByDepartmentUserResp.RequestId())
                        return nil, findByDepartmentUserResp.CodeError
                }

users = append(users, findByDepartmentUserResp.Data.Items...)
        }

// 返回结果
        return &ListUserByDepartmentResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                ChildrenDepartmentResponse:   childrenDepartmentResp.Data,
                FindByDepartmentUserResponse: users,
        }, nil
}
```

## [创建多维表格同时添加数据表](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/base/create_app_and_tables.go)

创建多维表格同时添加数据表，使用到两个 OpenAPI：[创建多维表格](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/create)和[新增一个数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/create)：

```java
package base

import (
        "context"
        "fmt"

lark "github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
        larkbitable "github.com/larksuite/oapi-sdk-go/v3/service/bitable/v1"
)

type CreateAppAndTablesRequest struct {
        Name        string
        FolderToken string
        Tables      []*larkbitable.ReqTable
}

type CreateAppAndTablesResponse struct {
        *larkcore.CodeError
        CreateAppResponse       *larkbitable.CreateAppRespData
        CreateAppTablesResponse []*larkbitable.CreateAppTableRespData
}

// CreateAppAndTables 创建多维表格同时添加数据表
func CreateAppAndTables(client *lark.Client, request *CreateAppAndTablesRequest) (*CreateAppAndTablesResponse, error) {
        // 创建多维表格
        createAppReq := larkbitable.NewCreateAppReqBuilder().
                ReqApp(larkbitable.NewReqAppBuilder().
                        Name(request.Name).
                        FolderToken(request.FolderToken).
                        Build()).
                Build()

createAppResp, err := client.Bitable.App.Create(context.Background(), createAppReq)
        if err != nil {
                return nil, err
        }
        if !createAppResp.Success() {
                fmt.Printf("client.Bitable.App.Create failed, code: %d, msg: %s, log_id: %s\n",
                        createAppResp.Code, createAppResp.Msg, createAppResp.RequestId())
                return nil, createAppResp.CodeError
        }

// 添加数据表
        tables := make([]*larkbitable.CreateAppTableRespData, 0)
        for _, table := range request.Tables {
                req := larkbitable.NewCreateAppTableReqBuilder().
                        AppToken(*createAppResp.Data.App.AppToken).
                        Body(larkbitable.NewCreateAppTableReqBodyBuilder().
                                Table(table).
                                Build()).
                        Build()

createAppTableResp, err := client.Bitable.AppTable.Create(context.Background(), req)
                if err != nil {
                        return nil, err
                }
                if !createAppTableResp.Success() {
                        fmt.Printf("client.Bitable.AppTable.Create failed, code: %d, msg: %s, log_id: %s\n",
                                createAppTableResp.Code, createAppTableResp.Msg, createAppTableResp.RequestId())
                        return nil, createAppTableResp.CodeError
                }

tables = append(tables, createAppTableResp.Data)
        }

// 返回结果
        return &CreateAppAndTablesResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                CreateAppResponse:       createAppResp.Data,
                CreateAppTablesResponse: tables,
        }, nil
}
```

## [复制粘贴某个范围的单元格数据](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/sheets/copy_and_paste_by_range.go)

复制粘贴某个范围的单元格数据，使用到两个OpenAPI：[读取单个范围](https://open.feishu.cn/document/ukTMukTMukTM/ugTMzUjL4EzM14COxMTN)和[向单个范围写入数据](https://open.feishu.cn/document/ukTMukTMukTM/uAjMzUjLwIzM14CMyMTN)。

```java
package sheets

import (
        "context"
        "encoding/json"
        "fmt"
        "net/http"

lark "github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
)

type CopyAndPasteByRangeRequest struct {
        SpreadsheetToken string
        SrcRange         string
        DstRange         string
}

type CopyAndPasteRangeResponse struct {
        *larkcore.CodeError
        ReadResponse  *SpreadsheetRespData
        WriteResponse *SpreadsheetRespData
}

// CopyAndPasteRange 复制粘贴某个范围的单元格数据
func CopyAndPasteRange(client *lark.Client, request *CopyAndPasteByRangeRequest) (*CopyAndPasteRangeResponse, error) {
        // 读取单个范围
        readResp, err := client.Do(context.Background(), &larkcore.ApiReq{
                HttpMethod:                http.MethodGet,
                ApiPath:                   fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values/%s", request.SpreadsheetToken, request.SrcRange),
                SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeTenant},
        })
        if err != nil {
                return nil, err
        }

readSpreadsheetResp := &SpreadsheetResp{}
        err = json.Unmarshal(readResp.RawBody, readSpreadsheetResp)
        if err != nil {
                return nil, err
        }
        readSpreadsheetResp.ApiResp = readResp
        if readSpreadsheetResp.Code != 0 {
                fmt.Printf("read spreadsheet failed, code: %d, msg: %s, log_id: %s\n",
                        readSpreadsheetResp.Code, readSpreadsheetResp.Msg, readSpreadsheetResp.RequestId())
                return nil, readSpreadsheetResp.CodeError
        }

// 向单个范围写入数据
        valueRange := map[string]interface{}{}
        valueRange["range"] = request.DstRange
        valueRange["values"] = readSpreadsheetResp.Data.ValueRange.Values
        body := map[string]interface{}{}
        body["valueRange"] = valueRange

writeResp, err := client.Do(context.Background(), &larkcore.ApiReq{
                HttpMethod:                http.MethodPut,
                ApiPath:                   fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values", request.SpreadsheetToken),
                Body:                      body,
                SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeTenant},
        })
        if err != nil {
                return nil, err
        }

writeSpreadsheetResp := &SpreadsheetResp{}
        err = json.Unmarshal(writeResp.RawBody, writeSpreadsheetResp)
        if err != nil {
                return nil, err
        }
        writeSpreadsheetResp.ApiResp = writeResp
        if writeSpreadsheetResp.Code != 0 {
                fmt.Printf("write spreadsheet failed, code: %d, msg: %s, log_id: %s\n",
                        writeSpreadsheetResp.Code, writeSpreadsheetResp.Msg, writeSpreadsheetResp.RequestId())
                return nil, writeSpreadsheetResp.CodeError
        }

// 返回结果
        return &CopyAndPasteRangeResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                ReadResponse:  readSpreadsheetResp.Data,
                WriteResponse: writeSpreadsheetResp.Data,
        }, nil
}
```

## [下载指定范围单元格的所有素材列表](https://github.com/larksuite/oapi-sdk-go-demo/blob/main/composite_api/sheets/download_media_by_range.go)

下载指定范围单元格的所有素材列表，使用到两个OpenAPI：[读取单个范围](https://open.feishu.cn/document/ukTMukTMukTM/ugTMzUjL4EzM14COxMTN)和[下载素材](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/download)：

```java
package sheets

import (
        "context"
        "encoding/json"
        "fmt"
        "net/http"
        "reflect"

lark "github.com/larksuite/oapi-sdk-go/v3"
        larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
        larkdrive "github.com/larksuite/oapi-sdk-go/v3/service/drive/v1"
)

type DownloadMediaByRangeRequest struct {
        SpreadsheetToken string
        Range            string
}

type DownloadMediaByRangeResponse struct {
        *larkcore.CodeError
        ReadResponse          *SpreadsheetRespData
        DownloadMediaResponse []*larkdrive.DownloadMediaResp
}

func DownloadMediaByRange(client *lark.Client, request *DownloadMediaByRangeRequest) (*DownloadMediaByRangeResponse, error) {
        // 读取单个范围
        readResp, err := client.Do(context.Background(), &larkcore.ApiReq{
                HttpMethod:                http.MethodGet,
                ApiPath:                   fmt.Sprintf("/open-apis/sheets/v2/spreadsheets/%s/values/%s", request.SpreadsheetToken, request.Range),
                SupportedAccessTokenTypes: []larkcore.AccessTokenType{larkcore.AccessTokenTypeTenant},
        })
        if err != nil {
                return nil, err
        }

readSpreadsheetResp := &SpreadsheetResp{}
        err = json.Unmarshal(readResp.RawBody, readSpreadsheetResp)
        if err != nil {
                return nil, err
        }
        readSpreadsheetResp.ApiResp = readResp
        if readSpreadsheetResp.Code != 0 {
                fmt.Printf("read spreadsheet failed, code: %d, msg: %s, log_id: %s\n",
                        readSpreadsheetResp.Code, readSpreadsheetResp.Msg, readSpreadsheetResp.RequestId())
                return nil, readSpreadsheetResp.CodeError
        }

// 下载文件
        files := make([]*larkdrive.DownloadMediaResp, 0)
        values := readSpreadsheetResp.Data.ValueRange.Values
        tokens := parseFileToken(values, make(map[string]bool))
        for _, token := range tokens {
                downloadMediaReq := larkdrive.NewDownloadMediaReqBuilder().
                        FileToken(token).
                        Build()

downloadMediaResp, err := client.Drive.Media.Download(context.Background(), downloadMediaReq)
                if err != nil {
                        return nil, err
                }
                if !downloadMediaResp.Success() {
                        fmt.Printf("client.Drive.Media.Download failed, code: %d, msg: %s, log_id: %s\n",
                                downloadMediaResp.Code, downloadMediaResp.Msg, downloadMediaResp.RequestId())
                        return nil, downloadMediaResp.CodeError
                }

files = append(files, downloadMediaResp)
        }

// 返回结果
        return &DownloadMediaByRangeResponse{
                CodeError: &larkcore.CodeError{
                        Code: 0,
                        Msg:  "success",
                },
                ReadResponse:          readSpreadsheetResp.Data,
                DownloadMediaResponse: files,
        }, nil
}

func parseFileToken(values []interface{}, tokens map[string]bool) []string {
        if len(values) == 0 {
                res := make([]string, 0, len(tokens))
                for k := range tokens {
                        res = append(res, k)
                }
                return res
        }
        for _, i := range values {
                kind := reflect.TypeOf(i).Kind()
                if kind == reflect.Slice {
                        parseFileToken(i.([]interface{}), tokens)
                } else if kind == reflect.Map {
                        m := i.(map[string]interface{})
                        if val, ok := m["fileToken"]; ok {
                                tokens[val.(string)] = true
                        }
                }
        }

res := make([]string, 0, len(tokens))
        for k := range tokens {
                res = append(res, k)
        }
        return res
}
```
