├── .gitignore ├── LICENSE ├── README.md ├── core ├── consts │ └── consts.go ├── model │ └── vo │ │ ├── app_vo.go │ │ ├── auth_vo.go │ │ ├── calendar_v4_vo.go │ │ ├── calendar_vo.go │ │ ├── chat_vo.go │ │ ├── document_vo.go │ │ ├── order_vo.go │ │ ├── robot_chat_vo.go │ │ ├── robot_message_vo.go │ │ ├── role_vo.go │ │ ├── scopes_vo.go │ │ ├── user_vo.go │ │ └── vo.go └── util │ ├── encrypt │ ├── aes.go │ ├── aes_test.go │ ├── encoder.go │ └── hmac.go │ ├── file │ └── file.go │ ├── http │ ├── http.go │ └── http_test.go │ ├── json │ └── json.go │ └── log │ └── log.go ├── go.mod ├── go.sum ├── sdk ├── app.go ├── app_test.go ├── auth.go ├── auth_test.go ├── calendar.go ├── calendar_test.go ├── calendar_v4.go ├── calendar_v4_test.go ├── chat.go ├── chat_test.go ├── department.go ├── department_test.go ├── document.go ├── document_test.go ├── image.go ├── image_test.go ├── order.go ├── order_test.go ├── robot_chat.go ├── robot_chat_test.go ├── robot_send_msg.go ├── robot_send_msg_test.go ├── role.go ├── role_test.go ├── scopes.go ├── scopes_test.go ├── sdk.go ├── user.go └── user_test.go └── test └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | /.idea 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 galaxy-book 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduce 2 | FeiShu ([https://feishu.cn](https://feishu.cn)) Server Api Sdk For Go. 3 | 4 | ## Doc 5 | FeiShu Server Api Doc ([https://open.feishu.cn/document/ukTMukTMukTM/uADN14CM0UjLwQTN](https://open.feishu.cn/document/ukTMukTMukTM/uADN14CM0UjLwQTN)) 6 | 7 | ## Feature 8 | - [x] 授权 9 | - [x] 获取 app_access_token(企业自建应用) 10 | - [x] 获取 app_access_token(应用商店应用) 11 | - [x] 获取 tenant_access_token(企业自建应用) 12 | - [x] 获取 tenant_access_token(应用商店应用) 13 | - [x] 重新推送 app_ticket 14 | - [x] [查询租户授权状态](https://bytedance.feishu.cn/docs/doccnHJx2UbLZh5kiWjNawICyNd#dCNL6V) 15 | - [x] [申请授权](https://bytedance.feishu.cn/docs/doccnHJx2UbLZh5kiWjNawICyNd#kHHiAa) 16 | - [ ] 身份验证 17 | - [ ] 请求身份验证 18 | - [x] 获取登录用户身份 19 | - [x] 刷新access_token 20 | - [x] 获取用户信息 21 | - [ ] 通讯录 22 | - [x] 获取通讯录授权范围 23 | - [x] 获取子部门列表 24 | - [ ] 获取子部门 ID 列表 25 | - [x] 获取部门详情 26 | - [ ] 批量获取部门详情 27 | - [x] 获取部门用户列表 28 | - [x] 获取部门用户详情 29 | - [ ] 获取企业自定义用户属性配置 30 | - [x] 批量获取用户信息 31 | - [ ] 新增用户 32 | - [ ] 更新用户信息 33 | - [ ] 删除用户 34 | - [ ] 新增部门 35 | - [ ] 更新部门信息 36 | - [ ] 删除部门 37 | - [ ] 批量新增用户 38 | - [ ] 批量新增部门 39 | - [ ] 查询批量任务执行状态 40 | - [ ] 获取应用管理员管理范围 41 | - [ ] 获取角色列表 42 | - [ ] 获取角色成员列表 43 | - [ ] 用户信息 44 | - [ ] 使用手机号或邮箱获取用户 ID 45 | - [ ] 应用信息 46 | - [ ] 获取应用管理权限 47 | - [ ] 获取应用在企业内的可用范围 48 | - [ ] 获取用户可用的应用 49 | - [ ] 获取企业安装的应用 50 | - [ ] 更新应用可用范围 51 | - [ ] 设置用户可用应用 52 | - [x] 应用商店计费信息 53 | - [x] 查询用户是否在应用开通范围 54 | - [x] 查询租户购买的付费方案 55 | - [x] 查询订单详情 56 | - [x] 群组 57 | - [x] 获取用户所在的群列表 58 | - [x] 获取群成员列表 59 | - [x] 搜索用户所在的群列表 60 | - [ ] 机器人 61 | - [x] 批量发送消息 62 | - [x] 发送文本消息 63 | - [x] 发送图片消息 64 | - [x] 发送富文本消息 65 | - [ ] 发送群名片 66 | - [ ] 消息撤回 67 | - [x] 发送卡片通知 68 | - [x] 群信息和群管理 69 | - [x] 创建群 70 | - [x] 获取群列表 71 | - [x] 获取群信息 72 | - [x] 更新群信息 73 | - [x] 拉用户进群 74 | - [x] 移除用户出群 75 | - [x] 解散群 76 | - [x] 机器人信息与管理 77 | - [x] 拉机器人进群 78 | - [x] 日历 79 | - [x] 获取日历 80 | - [x] 获取日历列表 81 | - [x] 创建日历 82 | - [x] 删除日历 83 | - [x] 更新日历 84 | - [x] 获取日程 85 | - [x] 创建日程 86 | - [x] 获取日程列表 87 | - [x] 删除日程 88 | - [x] 更新日程 89 | - [x] 邀请/移除日程参与者 90 | - [x] 企业 91 | - [x] 获取企业信息 92 | 93 | ## 参考 94 | * 飞书官方文档 https://open.feishu.cn/document/uQjL04CN/ucDOz4yN4MjL3gzM 95 | -------------------------------------------------------------------------------- /core/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | //API Host const, v3 4 | const ( 5 | //获取 app_access_token(企业自建应用) 6 | ApiAppAccessTokenInternal = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/" 7 | //获取 app_access_token(应用商店应用) 8 | ApiAppAccessToken = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/" 9 | //获取 tenant_access_token(应用商店应用) 10 | ApiTenantAccessToken = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/" 11 | //获取 tenant_access_token(企业自建应用) 12 | ApiTenantAccessTokenInternal = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/" 13 | //重新推送 app_ticket 14 | ApiAppTicketResend = "https://open.feishu.cn/open-apis/auth/v3/app_ticket/resend/" 15 | //获取登录用户身份 16 | ApiOAuth2AccessToken = "https://open.feishu.cn/connect/qrconnect/oauth2/access_token/" 17 | //获取登录用户身份(通过user_access_token) 18 | ApiOAuth2GetUserInfoByAccessToken = "https://open.feishu.cn/connect/qrconnect/oauth2/user_info/" 19 | //code2session 20 | ApiTokenLoginValidate = "https://open.feishu.cn/open-apis/mina/v2/tokenLoginValidate" 21 | //刷新access_token 22 | ApiRefreshAccessToken = "https://open.feishu.cn/open-apis/authen/v1/refresh_access_token" 23 | //获取登录用户身份(新版) 24 | ApiAuthenAccessToken = "https://open.feishu.cn/open-apis/authen/v1/access_token" 25 | //JSAPI 临时授权凭证 26 | ApiJSApiTicket = "https://open.feishu.cn/open-apis/jssdk/ticket/get" 27 | 28 | //////////////////部门和用户 29 | //获取通讯录授权范围 30 | ApiScope = "https://open.feishu.cn/open-apis/contact/v1/scope/get" 31 | //获取通讯录授权范围v2 32 | ApiScopeV2 = "https://open.feishu.cn/open-apis/contact/v2/scope/get" 33 | //使用手机号或邮箱获取用户 ID 34 | ApiBatchGetUserId = "https://open.feishu.cn/open-apis/user/v1/batch_get_id" 35 | 36 | //获取部门列表 37 | ApiDepartmentSimpleList = "https://open.feishu.cn/open-apis/contact/v1/department/simple/list" 38 | //获取部门列表 v2 39 | ApiDepartmentSimpleListV2 = "https://open.feishu.cn/open-apis/contact/v2/department/simple/list" 40 | 41 | //获取部门详情 42 | ApiDepartmentInfoGet = "https://open.feishu.cn/open-apis/contact/v1/department/info/get" 43 | //批量获取部门详情 44 | ApiDepartmentInfoBatchGet = "https://open.feishu.cn/open-apis/contact/v2/department/detail/batch_get" 45 | 46 | //获取部门用户列表 47 | ApiDepartmentUserList = "https://open.feishu.cn/open-apis/contact/v1/department/user/list" 48 | //获取部门用户列表v2 49 | ApiDepartmentUserListV2 = "https://open.feishu.cn/open-apis/contact/v2/department/user/list" 50 | //获取用户详情 51 | ApiDepartmentUserDetailList = "https://open.feishu.cn/open-apis/contact/v1/department/user/detail/list" 52 | //获取用户详情v2 53 | ApiDepartmentUserDetailListV2 = "https://open.feishu.cn/open-apis/contact/v2/department/user/detail/list" 54 | 55 | //批量获取用户信息 56 | ApiUserBatchGet = "https://open.feishu.cn/open-apis/contact/v1/user/batch_get" 57 | //批量获取用户信息v2 58 | ApiUserBatchGetV2 = "https://open.feishu.cn/open-apis/contact/v2/user/batch_get" 59 | //获取用户列表v3 60 | ApiUserListV3 = "https://open.feishu.cn/open-apis/contact/v3/users" 61 | 62 | //////////////////机器人发送消息 63 | //机器人发送消息 64 | ApiRobotSendMessage = "https://open.feishu.cn/open-apis/message/v4/send/" 65 | //机器人批量发送消息 66 | ApiRobotSendBatchMessage = "https://open.feishu.cn/open-apis/message/v4/batch_send/" 67 | 68 | //////////////////角色 69 | //获取角色列表 70 | ApiRoleList = "https://open.feishu.cn/open-apis/contact/v2/role/list" 71 | //获取角色成员列表 72 | ApiRoleMemberList = "https://open.feishu.cn/open-apis/contact/v2/role/members" 73 | 74 | ///////////////////////////////////////////////////////// 75 | //创建日历 76 | ApiCalendarCreate = "https://open.feishu.cn/open-apis/calendar/v3/calendars" 77 | //创建日历v4版本 78 | ApiCalendarCreateV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars" 79 | //获取日历 80 | ApiCalendarGet = "https://open.feishu.cn/open-apis/calendar/v3/calendar_list/%s" 81 | //获取日历列表 82 | ApiCalendarListGet = "https://open.feishu.cn/open-apis/calendar/v3/calendar_list" 83 | //更新日历 84 | ApiCalendarUpdate = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s" 85 | //创建日程 86 | ApiCalendarEventCreate = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s/events" 87 | //删除日程 88 | ApiCalendarEventDelete = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s/events/%s" 89 | //邀请/移除日程参与者 90 | ApiCalendarEventAttendeesUpdate = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s/events/%s/attendees" 91 | //获取访问控制列表 92 | ApiCalendarAttendeesGet = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s/acl" 93 | //删除访问空值 94 | ApiCalendarAttendeesDelete = "https://open.feishu.cn/open-apis/calendar/v3/calendars/%s/acl/%s" 95 | 96 | //创建日程v4 97 | ApiCalendarEventCreateV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events" 98 | //删除日程v4 99 | ApiCalendarEventDeleteV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s" 100 | //获取日程v4 101 | ApiCalendarEventGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s" 102 | //获取日程列表v4 103 | ApiCalendarEventBatchGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events" 104 | //更新日程v4 105 | ApiCalendarEventUpdateV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s" 106 | //搜索日程v4 107 | ApiCalendarEventSearchV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/search" 108 | //创建日程参与人v4 109 | ApiCalendarEventAttendeesAddV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s/attendees" 110 | //删除日程参与人v4 111 | ApiCalendarEventAttendeesDeleteV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s/attendees/batch_delete" 112 | //获取日程参与人列表v4 113 | ApiCalendarEventAttendeesGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s/attendees" 114 | //获取参与人群成员列表v4 115 | ApiCalendarEventAttendeesChatMembersGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/events/%s/attendees/%s/chat_members" 116 | 117 | ApiCalendarGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s" 118 | ApiCalendarDeleteV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s" 119 | ApiCalendarGetListV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars" 120 | ApiCalendarUpdateV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s" 121 | ApiCalendarSearchV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/search" 122 | ApiCalendarUnsubscribeV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/unsubscribe" 123 | ApiCalendarSubscribeV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/subscribe" 124 | ApiCalendarSubscriptionV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/subscription" 125 | //创建访问控制 126 | ApiCalendarAddCalendarAclV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/acls" 127 | //删除访问控制 128 | ApiCalendarDeleteCalendarAclV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/acls/%s" 129 | //获取访问控制列表 130 | ApiCalendarCalendarAclGetV4 = "https://open.feishu.cn/open-apis/calendar/v4/calendars/%s/acls" 131 | 132 | //搜索用户 133 | ApiSearchUser = "https://open.feishu.cn/open-apis/search/v1/user" 134 | 135 | //检验应用管理员 136 | ApiIsUserAdmin = "https://open.feishu.cn/open-apis/application/v3/is_user_admin" 137 | //查询应用管理员列表 138 | ApiAdminUserList = "https://open.feishu.cn/open-apis/user/v4/app_admin_user/list" 139 | //获取企业信息 140 | ApiOrgInfo = "https://open.feishu.cn/open-apis/tenant/v2/tenant/query" 141 | 142 | ////////用户群组 143 | //获取用户所在的群列表 144 | ApiUserGroupLIst = "https://open.feishu.cn/open-apis/user/v4/group_list" 145 | //获取群成员列表 146 | ApiChatMembers = "https://open.feishu.cn/open-apis/chat/v4/members" 147 | //搜索用户所在的群列表 148 | ApiChatSearch = "https://open.feishu.cn/open-apis/chat/v4/search" 149 | //获取用户或机器人所在的群列表 150 | ApiImChatList = "https://open.feishu.cn/open-apis/im/v1/chats" 151 | //获取群信息 152 | ApiImChatInfo = "https://open.feishu.cn/open-apis/im/v1/chats/" 153 | 154 | ////////群信息和群管理 155 | //创建群 156 | ApiCreateChat = "https://open.feishu.cn/open-apis/chat/v4/create/" 157 | //获取群列表 158 | ApiChatList = "https://open.feishu.cn/open-apis/chat/v4/list" 159 | //获取群信息 160 | ApiChatInfo = "https://open.feishu.cn/open-apis/chat/v4/info" 161 | //更新群信息 162 | ApiUpdateChat = "https://open.feishu.cn/open-apis/chat/v4/update/" 163 | //拉用户进群 164 | ApiAddChatUser = "https://open.feishu.cn/open-apis/chat/v4/chatter/add/" 165 | //移除用户出群 166 | ApiRemoveChatUser = "https://open.feishu.cn/open-apis/chat/v4/chatter/delete/" 167 | //解散群 168 | ApiDisbandChat = "https://open.feishu.cn/open-apis/chat/v4/disband" 169 | //拉机器人进群 170 | ApiAddBot = "https://open.feishu.cn/open-apis/bot/v4/add" 171 | 172 | /////////订单 173 | //查询用户是否在应用开通范围 174 | ApiCheckUser = "https://open.feishu.cn/open-apis/pay/v1/paid_scope/check_user" 175 | ApiGetOrderList = "https://open.feishu.cn/open-apis/pay/v1/order/list" 176 | ApiGetOrderInfo = "https://open.feishu.cn/open-apis/pay/v1/order/get" 177 | 178 | ////////云文档 179 | //查询文档 180 | ApiSearchDocs = "https://open.feishu.cn/open-apis/suite/docs-api/search/object" 181 | //获取云文档信息 182 | ApiGetDocMeta = "https://open.feishu.cn/open-apis/doc/v2/meta" 183 | //获取图片 184 | ApiGetImage = "https://open.feishu.cn/open-apis/image/v4/get" 185 | 186 | /// 授权状态 187 | // https://bytedance.feishu.cn/docs/doccnHJx2UbLZh5kiWjNawICyNd# 188 | ApiGetScopes = "https://open.feishu.cn/open-apis/application/v6/scopes" 189 | ApiApplyScopes = "https://open.feishu.cn/open-apis/application/v6/scopes/apply" 190 | ) 191 | 192 | //Other Const 193 | const ( 194 | TestAppId = "**********************" 195 | TestAppSecret = "******************************" 196 | TestTicket = "**********************************" 197 | ) 198 | 199 | const ( 200 | AccessRoleReader = "reader" 201 | AccessRoleFreeBusyReader = "free_busy_reader" 202 | AccessRoleWriter = "writer" 203 | AccessRoleOwner = "owner" 204 | ) 205 | 206 | const ( 207 | ActionInvite = "invite" 208 | ActionRemove = "remove" 209 | ) 210 | -------------------------------------------------------------------------------- /core/model/vo/app_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type IsUserAdminResp struct { 4 | CommonVo 5 | Data IsUserAdminRespData `json:"data"` 6 | } 7 | 8 | type IsUserAdminRespData struct { 9 | IsAppAdmin bool `json:"is_app_admin"` 10 | } 11 | 12 | type AdminUserListResp struct { 13 | CommonVo 14 | Data AdminUserListRespData `json:"data"` 15 | } 16 | 17 | type AdminUserListRespData struct { 18 | UserList []AdminUserData `json:"user_list"` 19 | } 20 | 21 | type AdminUserData struct { 22 | OpenId string `json:"open_id"` 23 | UserId string `json:"user_id"` 24 | UnionId string `json:"union_id"` 25 | } 26 | 27 | type OrgInfoResp struct { 28 | CommonVo 29 | Data OrgInfoData `json:"data"` 30 | } 31 | 32 | type OrgInfoData struct { 33 | Tenant TenantData `json:"tenant"` 34 | } 35 | 36 | type TenantData struct { 37 | Name string `json:"name"` 38 | DisplayId string `json:"display_id"` 39 | TenantTag int `json:"tenant_tag"` 40 | TenantKey string `json:"tenant_key"` 41 | Avatar UserAvatar `json:"avatar"` 42 | } 43 | 44 | type GetJsTicketResp struct { 45 | CommonVo 46 | Data GetJsTicketData `json:"data"` 47 | } 48 | 49 | type GetJsTicketData struct { 50 | Ticket string `json:"ticket"` 51 | ExpireIn int `json:"expire_in"` 52 | } 53 | -------------------------------------------------------------------------------- /core/model/vo/auth_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type AppAccessTokenInternalRespVo struct { 4 | CommonVo 5 | TenantAccessToken string `json:"tenant_access_token"` 6 | AppAccessToken string `json:"app_access_token"` 7 | Expire int64 `json:"expire"` 8 | } 9 | 10 | type AppAccessTokenRespVo struct { 11 | CommonVo 12 | AppAccessToken string `json:"app_access_token"` 13 | Expire int64 `json:"expire"` 14 | } 15 | 16 | type TenantAccessTokenRespVo struct { 17 | CommonVo 18 | TenantAccessToken string `json:"tenant_access_token"` 19 | Expire int64 `json:"expire"` 20 | } 21 | 22 | type OAuth2AccessTokenReqVo struct { 23 | AppId string `json:"app_id"` 24 | AppSecret string `json:"app_secret"` 25 | AppAccessToken string `json:"app_access_token"` 26 | GrantType string `json:"grant_type"` 27 | Code string `json:"code"` 28 | RefreshToken string `json:"refresh_token"` 29 | } 30 | 31 | type OAuth2AccessTokenRespVo struct { 32 | AccessToken string `json:"access_token"` 33 | AvatarUrl string `json:"avatar_url"` 34 | ExpiresIn int64 `json:"expires_in"` 35 | Name string `json:"name"` 36 | EnName string `json:"en_name"` 37 | OpenId string `json:"open_id"` 38 | TenantKey string `json:"tenant_key"` 39 | RefreshToken string `json:"refresh_token"` 40 | TokenType string `json:"token_type"` 41 | Code int `json:"code"` 42 | Message string `json:"message"` 43 | } 44 | 45 | type OAuth2UserInfoRespVo struct { 46 | AvatarUrl string `json:"AvatarUrl"` 47 | Name string `json:"Name"` 48 | Email string `json:"Email"` 49 | Status int `json:"Status"` 50 | EmployeeId string `json:"EmployeeId"` 51 | Mobile string `json:"Mobile"` 52 | } 53 | 54 | type TokenLoginValidateResp struct { 55 | Data TokenLoginValidateRespData `json:"data"` 56 | 57 | CommonVo 58 | } 59 | 60 | type TokenLoginValidateRespData struct { 61 | Uid string `json:"uid"` 62 | OpenId string `json:"open_id"` 63 | UnionId string `json:"union_id"` 64 | SessionKey string `json:"session_key"` 65 | TenantKey string `json:"tenant_key"` 66 | EmployeeId string `json:"employee_id"` 67 | TokenType string `json:"token_type"` 68 | AccessToken string `json:"access_token"` 69 | ExpiresIn int64 `json:"expires_in"` 70 | RefreshToken string `json:"refresh_token"` 71 | } 72 | 73 | type RefreshAccessTokenResp struct { 74 | CommonVo 75 | Data RefreshAccessTokenRespData `json:"data"` 76 | } 77 | 78 | type RefreshAccessTokenRespData struct { 79 | AccessToken string `json:"access_token"` 80 | AvatarUrl string `json:"avatar_url"` 81 | ExpiresIn int64 `json:"expires_in"` 82 | Name string `json:"name"` 83 | EnName string `json:"en_name"` 84 | OpenId string `json:"open_id"` 85 | TenantKey string `json:"tenant_key"` 86 | RefreshExpiresIn int64 `json:"refresh_expires_in"` 87 | RefreshToken string `json:"refresh_token"` 88 | TokenType string `json:"token_type"` 89 | } 90 | -------------------------------------------------------------------------------- /core/model/vo/calendar_v4_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type CreateCalendarV4Req struct { 4 | Summary string `json:"summary"` 5 | Description string `json:"description,omitempty"` 6 | Permissions string `json:"permissions"` 7 | Color int32 `json:"color"` 8 | SummaryAlias string `json:"summary_alias"` 9 | } 10 | 11 | type CommonCalendarV4Resp struct { 12 | CommonVo 13 | Data CalendarV4 `json:"data"` 14 | } 15 | 16 | type CalendarV4 struct { 17 | Calendar CalendarV4Data `json:"calendar"` 18 | } 19 | 20 | type CalendarV4Data struct { 21 | Description string `json:"description"` 22 | Type string `json:"type"` 23 | Summary string `json:"summary"` 24 | Permissions string `json:"permissions"` 25 | Color int32 `json:"color"` 26 | SummaryAlias string `json:"summary_alias"` 27 | IsDeleted bool `json:"is_deleted"` 28 | Role string `json:"role"` 29 | IsThirdParty bool `json:"is_third_party"` 30 | CalendarId string `json:"calendar_id"` 31 | } 32 | 33 | type VoidV4Resp struct { 34 | CommonVo 35 | Data string `json:"data"` 36 | } 37 | 38 | type DeleteCalendarV4Req struct { 39 | CalendarId string `json:"calendar_id"` 40 | } 41 | 42 | type GetCalendarV4Req struct { 43 | CalendarId string `json:"calendar_id"` 44 | } 45 | 46 | type GetCalendarV4Resp struct { 47 | CommonVo 48 | Data CalendarV4Data `json:"data"` 49 | } 50 | 51 | type GetCalendarListV4Req struct { 52 | PageSize int `json:"page_size"` 53 | PageToken string `json:"page_token"` 54 | SyncToken string `json:"sync_token"` 55 | } 56 | 57 | type GetCalendarListV4Resp struct { 58 | CommonVo 59 | Data GetCalendarListV4RespData `json:"data"` 60 | } 61 | 62 | type GetCalendarListV4RespData struct { 63 | PageToken string `json:"page_token"` 64 | SyncToken string `json:"sync_token"` 65 | HasMore bool `json:"has_more"` 66 | CalendarList []GetCalendarListV4RespDataCalendarItem `json:"calendar_list"` 67 | } 68 | 69 | type GetCalendarListV4RespDataCalendarItem struct { 70 | CalendarID string `json:"calendar_id"` 71 | Color int64 `json:"color"` 72 | Permissions string `json:"permissions"` 73 | Role string `json:"role"` 74 | Summary string `json:"summary"` 75 | Type string `json:"type"` 76 | } 77 | 78 | type UpdateCalendarV4Req struct { 79 | Summary string `json:"summary"` 80 | Description string `json:"description"` 81 | Permissions string `json:"permissions"` 82 | Color int `json:"color"` 83 | SummaryAlias string `json:"summary_alias"` 84 | CalendarId string `json:"calendar_id"` 85 | } 86 | 87 | type SearchCalendarV4Req struct { 88 | Query string `json:"query"` 89 | } 90 | 91 | type SearchCalendarV4Resp struct { 92 | CommonVo 93 | Data SearchCalendarV4RespData `data` 94 | } 95 | type SearchCalendarV4RespData struct { 96 | PageToken string `json:"page_token"` 97 | Items []SearchCalendarV4RespDataItem `json:"items"` 98 | } 99 | type SearchCalendarV4RespDataItem struct { 100 | CalendarID string `json:"calendar_id"` 101 | Summary string `json:"summary"` 102 | Type string `json:"type"` 103 | } 104 | 105 | type UnsubscribeCalendarV4Req struct { 106 | CalendarId string `json:"calendar_id"` 107 | } 108 | 109 | type SubscribeCalendarV4Req struct { 110 | CalendarId string `json:"calendar_id"` 111 | } 112 | 113 | type SubscribeCalendarV4Resp struct { 114 | CommonVo 115 | Data SubscribeCalendarV4RespData `json:"data"` 116 | } 117 | type SubscribeCalendarV4RespData struct { 118 | Calendar SubscribeCalendarV4RespDataCalendar `json:"calendar"` 119 | } 120 | type SubscribeCalendarV4RespDataCalendar struct { 121 | CalendarID string `json:"calendar_id"` 122 | Color int64 `json:"color"` 123 | Role string `json:"role"` 124 | Summary string `json:"summary"` 125 | Type string `json:"type"` 126 | } 127 | 128 | type CreateCalendarEventV4Req struct { 129 | Summary string `json:"summary"` 130 | Description string `json:"description"` 131 | StartTime CalendarEventTime `json:"start_time"` 132 | EndTime CalendarEventTime `json:"end_time"` 133 | Vchat *Vchat `json:"vchat"` 134 | Visibility *string `json:"visibility"` //default(忙碌),public(公开),private(私密) 135 | AttendeeAbility *string `json:"attendee_ability"` //none,can_see_other,can_invite_others,can_modify_event 136 | FreeBusyStatus *string `json:"free_busy_status"` 137 | Location *Location `json:"location"` 138 | Color int32 `json:"color"` 139 | Reminders []CalendarEventReminder `json:"reminders"` 140 | Recurrence string `json:"recurrence"` 141 | Schemas []CalendarEventSchema `json:"schemas"` 142 | } 143 | 144 | type CalendarEventTime struct { 145 | Date string `json:"date"` 146 | Timestamp string `json:"timestamp"` 147 | Timezone string `json:"timezone"` 148 | } 149 | 150 | type Vchat struct { 151 | MeetingUrl string `json:"meeting_url"` 152 | } 153 | 154 | type Location struct { 155 | Name string `json:"name"` 156 | Address string `json:"address"` 157 | Latitude float64 `json:"latitude"` 158 | Longitude float64 `json:"longitude"` 159 | } 160 | 161 | type CalendarEventReminder struct { 162 | Minutes int64 `json:"minutes"` 163 | } 164 | 165 | type CalendarEventSchema struct { 166 | UiName string `json:"ui_name"` 167 | UiStatus string `json:"ui_status"` 168 | } 169 | 170 | type CalendarEventDataV4 struct { 171 | EventId string `json:"event_id"` 172 | Summary string `json:"summary"` 173 | Description string `json:"description"` 174 | StartTime CalendarEventTime `json:"start_time"` 175 | EndTime CalendarEventTime `json:"end_time"` 176 | Vchat Vchat `json:"vchat"` 177 | Visibility string `json:"visibility"` 178 | AttendeeAbility string `json:"attendee_ability"` 179 | FreeBusyStatus string `json:"free_busy_status"` 180 | Location Location `json:"location"` 181 | Color int32 `json:"color"` 182 | Reminders []CalendarEventReminder `json:"reminders"` 183 | Recurrence string `json:"recurrence"` 184 | Schemas []CalendarEventSchema `json:"schemas"` 185 | } 186 | 187 | type CalendarEventInfoV4Resp struct { 188 | CommonVo 189 | Data CalendarEventV4 `json:"data"` 190 | } 191 | 192 | type CalendarEventV4 struct { 193 | Event CalendarEventDataV4 `json:"event"` 194 | } 195 | 196 | type GetCalendarEventListV4Resp struct { 197 | CommonVo 198 | Data GetCalendarEventListV4 `json:"data"` 199 | } 200 | 201 | type GetCalendarEventListV4 struct { 202 | PageToken string `json:"page_token"` 203 | SyncToken string `json:"sync_token"` 204 | HasMore bool `json:"has_more"` 205 | Items []CalendarEventDataV4 `json:"items"` 206 | } 207 | 208 | type AddCalendarEventAttendeesV4Req struct { 209 | Attendees []AttendeesV4 `json:"attendees"` 210 | } 211 | 212 | type AddCalendarEventAttendeesV4Resp struct { 213 | CommonVo 214 | Data AddCalendarEventAttendeesDataV4 `json:"data"` 215 | } 216 | 217 | type AddCalendarEventAttendeesDataV4 struct { 218 | Attendees []AttendeesV4 `json:"attendees"` 219 | } 220 | 221 | type AttendeesV4 struct { 222 | Type string `json:"type"` 223 | IsOptional bool `json:"is_optional"` 224 | UserId string `json:"user_id"` 225 | AttendeeId string `json:"attendee_id"` 226 | RsvpStatus string `json:"rsvp_status"` 227 | IsOrganizer bool `json:"is_organizer"` 228 | IsExternal bool `json:"is_external"` 229 | DisplayName string `json:"display_name"` 230 | } 231 | 232 | type DeleteCalendarEventAttendeesV4Req struct { 233 | AttendeeIds []string `json:"attendee_ids"` 234 | } 235 | 236 | type GetCalendarEventAttendeesV4Resp struct { 237 | CommonVo 238 | Data GetCalendarEventAttendeesDataV4 `json:"data"` 239 | } 240 | 241 | type GetCalendarEventAttendeesDataV4 struct { 242 | HasMore bool `json:"has_more"` 243 | PageToken string `json:"page_token"` 244 | Items []AttendeesV4 `json:"items"` 245 | } 246 | 247 | type UpdateCalendarEventV4Req struct { 248 | Summary *string `json:"summary"` 249 | Description *string `json:"description"` 250 | StartTime *CalendarEventTime `json:"start_time"` 251 | EndTime *CalendarEventTime `json:"end_time"` 252 | Vchat *Vchat `json:"vchat"` 253 | Visibility *string `json:"visibility"` //default(忙碌),public(公开),private(私密) 254 | AttendeeAbility *string `json:"attendee_ability"` //none,can_see_other,can_invite_others,can_modify_event 255 | FreeBusyStatus *string `json:"free_busy_status"` 256 | Location *Location `json:"location"` 257 | Color *int32 `json:"color"` 258 | Reminders *[]CalendarEventReminder `json:"reminders"` 259 | Recurrence *string `json:"recurrence"` 260 | Schemas *[]CalendarEventSchema `json:"schemas"` 261 | } 262 | 263 | type AddCalendarAclV4Req struct { 264 | Role string `json:"role"` //unknown(未知),free_busy_reader(游客),reader(订阅者),writer(编辑者),owner(管理员) 265 | Scope AclScope `json:"scope"` 266 | } 267 | 268 | type AclScope struct { 269 | Type string `json:"type"` //user(用户) 270 | UserId string `json:"user_id"` 271 | } 272 | 273 | type AddCalendarAclV4Resp struct { 274 | CommonVo 275 | Data AddCalendarAclRespData `json:"data"` 276 | } 277 | 278 | type AddCalendarAclRespData struct { 279 | Role string `json:"role"` 280 | AclId string `json:"acl_id"` 281 | Scope AclScope `json:"scope"` 282 | } 283 | 284 | type GetCalendarAclListV4Resp struct { 285 | CommonVo 286 | Data GetCalendarAclListV4Data `json:"data"` 287 | } 288 | 289 | type GetCalendarAclListV4Data struct { 290 | Acls []AddCalendarAclRespData `json:"acls"` 291 | HasMore bool `json:"has_more"` 292 | PageToken string `json:"page_token"` 293 | } 294 | -------------------------------------------------------------------------------- /core/model/vo/calendar_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type Calendar struct { 4 | Id string `json:"id"` 5 | Summary string `json:"summary"` 6 | Description string `json:"description"` 7 | IsPrivate bool `json:"is_private"` 8 | DefaultAccessRole string `json:"default_access_role"` 9 | } 10 | 11 | type CommonCalendarResp struct { 12 | CommonVo 13 | Data Calendar `json:"data"` 14 | } 15 | 16 | type CalendarListResp struct { 17 | CommonVo 18 | PageToken string `json:"page_token"` 19 | SyncToken string `json:"sync_token"` 20 | Data CalendarList `json:"data"` 21 | } 22 | 23 | type CalendarList struct { 24 | Items []Calendar `json:"items"` 25 | } 26 | 27 | type CreateCalendarReq struct { 28 | Summary string `json:"summary"` 29 | Description string `json:"description,omitempty"` 30 | IsPrivate bool `json:"is_private,omitempty"` 31 | DefaultAccessRole string `json:"default_access_role,omitempty"` 32 | } 33 | 34 | type UpdateCalendarReq struct { 35 | CalendarId string `json:"calendarId,omitempty"` 36 | Summary string `json:"summary,omitempty"` 37 | Description string `json:"description,omitempty"` 38 | IsPrivate bool `json:"is_private,omitempty"` 39 | DefaultAccessRole string `json:"default_access_role,omitempty"` 40 | } 41 | 42 | type CreateCalendarEventReq struct { 43 | Summary string `json:"summary"` 44 | Description string `json:"description,omitempty"` 45 | Start TimeFormat `json:"start"` 46 | End TimeFormat `json:"end"` 47 | Attendees *[]Attendees `json:"attendees,omitempty"` 48 | Visibility string `json:"visibility,omitempty"` 49 | } 50 | 51 | type TimeFormat struct { 52 | Date string `json:"date,omitempty"` 53 | TimeStamp int64 `json:"time_stamp,omitempty"` 54 | TimeZone string `json:"time_zone,omitempty"` 55 | } 56 | 57 | type Attendees struct { 58 | OpenId string `json:"open_id,omitempty"` 59 | EmployeeId string `json:"employee_id,omitempty"` 60 | DisplayName string `json:"display_name,omitempty"` 61 | Optional bool `json:"optional,omitempty"` 62 | } 63 | 64 | type CommonCalendarEventResp struct { 65 | CommonVo 66 | Data CalendarEvent `json:"data"` 67 | } 68 | 69 | type CalendarEvent struct { 70 | Id string `json:"id"` 71 | Summary string `json:"summary"` 72 | Description string `json:"description"` 73 | Start TimeFormat `json:"start"` 74 | End TimeFormat `json:"end"` 75 | Attendees []Attendees `json:"attendees"` 76 | Visibility string `json:"visibility"` 77 | } 78 | 79 | type CalendarEventListResp struct { 80 | CommonVo 81 | PageToken string `json:"page_token"` 82 | SyncToken string `json:"sync_token"` 83 | Data []CalendarEvent `json:"data"` 84 | } 85 | 86 | type AttendeesResp struct { 87 | Attendees 88 | Status string `json:"status"` 89 | } 90 | 91 | type UpdateCalendarEventAtendeesReq struct { 92 | Attendees []AttendeesResp `json:"attendees"` 93 | } 94 | 95 | type UpdateCalendarEventAtendeesResp struct { 96 | CommonVo 97 | Data []Attendees `json:"data"` 98 | } 99 | 100 | type GetCalendarAttendeesResp struct { 101 | CommonVo 102 | Data []CalendarAttendeesAcl `json:"data"` 103 | } 104 | 105 | type CalendarAttendeesAcl struct { 106 | Role string `json:"role"` 107 | Scope CalendarScope `json:"scope"` 108 | } 109 | 110 | type CalendarScope struct { 111 | Type string `json:"type"` 112 | OpenId string `json:"open_id,omitempty"` 113 | EmployeeId string `json:"employee_id,omitempty"` 114 | } 115 | 116 | type AddCalendarAttendeesAclReq struct { 117 | Role string `json:"role"` 118 | Scope CalendarScope `json:"scope"` 119 | } 120 | -------------------------------------------------------------------------------- /core/model/vo/chat_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type GroupListRespVo struct { 4 | CommonVo 5 | Data *UserGroupListData `json:"data"` 6 | } 7 | 8 | type UserGroupListData struct { 9 | HasMore bool `json:"has_more"` 10 | PageToken string `json:"page_token"` 11 | Groups []GroupData `json:"groups"` 12 | } 13 | 14 | type GroupData struct { 15 | Avatar string `json:"avatar"` 16 | ChatId string `json:"chat_id"` 17 | Description string `json:"description"` 18 | Name string `json:"name"` 19 | OwnerOpenId string `json:"owner_open_id"` 20 | OwnerUserId string `json:"owner_user_id"` 21 | } 22 | 23 | type ChatMembersRespVo struct { 24 | CommonVo 25 | Data *ChatGroupData `json:"data"` 26 | } 27 | 28 | type ChatGroupData struct { 29 | ChatId string `json:"chat_id"` 30 | HasMore bool `json:"has_more"` 31 | Members []MemberData `json:"members"` 32 | } 33 | 34 | type MemberData struct { 35 | OpenId string `json:"open_id"` 36 | UserId string `json:"user_id"` 37 | Name string `json:"name"` 38 | } 39 | 40 | type ImChatListRespVo struct { 41 | CommonVo 42 | Data ImChatListData `json:"data"` 43 | } 44 | 45 | type ImChatListData struct { 46 | Items []SimpleChatInfo `json:"items"` 47 | PageToken string `json:"page_token"` 48 | HasMore bool `json:"has_more"` 49 | } 50 | 51 | type SimpleChatInfo struct { 52 | ChatId string `json:"chat_id"` 53 | Avatar string `json:"avatar"` 54 | Name string `json:"name"` 55 | Description string `json:"description"` 56 | OwnerId string `json:"owner_id"` 57 | OwnerIdType string `json:"owner_id_type"` 58 | } 59 | 60 | type ImChatInfoRespVo struct { 61 | CommonVo 62 | Data ImChatInfoData `json:"data"` 63 | } 64 | 65 | type ImChatInfoData struct { 66 | Avatar string `json:"avatar"` 67 | Name string `json:"name"` 68 | Description string `json:"description"` 69 | I18nElements I18nElement `json:"i18n_elements,omitempty"` 70 | AddMemberPermission string `json:"add_member_permission"` 71 | ShareCardPermission string `json:"share_card_permission"` 72 | AtAllPermission string `json:"at_all_permission"` 73 | EditPermission string `json:"edit_permission"` 74 | OwnerIdType string `json:"owner_id_type"` 75 | OwnerId string `json:"owner_id"` 76 | ChatMode string `json:"chat_mode"` 77 | ChatType string `json:"chat_type"` 78 | ChatTag string `json:"chat_tag"` 79 | JoinMessageVisibility string `json:"join_message_visibility"` 80 | LeaveMessageVisibility string `json:"leave_message_visibility"` 81 | MembershipApproval string `json:"membership_approval"` 82 | ModerationPermission string `json:"moderation_permission"` 83 | External bool `json:"external"` 84 | TenantKey string `json:"tenant_key"` 85 | } 86 | -------------------------------------------------------------------------------- /core/model/vo/document_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type SearchDocsReqVo struct { 4 | SearchKey *string `json:"search_key"` 5 | Count *int `json:"count"` 6 | Offset *int `json:"offset"` 7 | OwnerIds *[]string `json:"owner_ids"` 8 | ChatIds *[]string `json:"chat_ids"` 9 | DocsTypes *[]string `json:"docs_types"` 10 | } 11 | 12 | type SearchDocsRespVo struct { 13 | CommonVo 14 | Data SearchDocsData `json:"data"` 15 | } 16 | 17 | type SearchDocsData struct { 18 | HasMore bool `json:"has_more"` 19 | Total int64 `json:"total"` 20 | DocsEntities []DocEntities `json:"docs_entities"` 21 | } 22 | 23 | type DocEntities struct { 24 | DocsToken string `json:"docs_token"` 25 | DocsType string `json:"docs_type"` 26 | Title string `json:"title"` 27 | OwnerId string `json:"owner_id"` 28 | } 29 | 30 | type GetDocMetaRespVo struct { 31 | CommonVo 32 | Data DocMetaData `json:"data"` 33 | } 34 | 35 | type DocMetaData struct { 36 | CreateDate string `json:"create_date"` 37 | CreateTime int64 `json:"create_time"` 38 | CreateUid string `json:"create_uid"` 39 | CreateUserName string `json:"create_user_name"` 40 | DeleteFlag int `json:"delete_flag"` 41 | EditTime int64 `json:"edit_time"` 42 | EditUserName string `json:"edit_user_name"` 43 | IsExternal bool `json:"is_external"` 44 | IsPined bool `json:"is_pined"` 45 | IsStared bool `json:"is_stared"` 46 | ObjType string `json:"obj_type"` 47 | OwnerId string `json:"owner_id"` 48 | OwnerUserName string `json:"owner_user_name"` 49 | ServerTime int64 `json:"server_time"` 50 | TenantId string `json:"tenant_id"` 51 | Title string `json:"title"` 52 | Type int `json:"type"` 53 | Url string `json:"url"` 54 | } 55 | -------------------------------------------------------------------------------- /core/model/vo/order_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type CheckUserReq struct { 4 | OpenId string `json:"open_id"` 5 | UserId string `json:"user_id"` 6 | } 7 | 8 | type CheckUserResp struct { 9 | CommonVo 10 | Data CheckUserData `json:"data"` 11 | } 12 | 13 | type CheckUserData struct { 14 | Status string `json:"status"` 15 | PricePlanId string `json:"price_plan_id"` 16 | IsTrial bool `json:"is_trial"` 17 | ServiceStopTime string `json:"service_stop_time"` 18 | } 19 | 20 | type GetOrderListReq struct { 21 | Status *string `json:"status"` 22 | PageSize int `json:"page_size"` 23 | PageToken *string `json:"page_token"` 24 | TenantKey *string `json:"tenant_key"` 25 | } 26 | 27 | type GetOrderListResp struct { 28 | CommonVo 29 | Data GetOrderListData `json:"data"` 30 | } 31 | 32 | type GetOrderListData struct { 33 | Total int64 `json:"total"` 34 | HasMore bool `json:"has_more"` 35 | PageToken string `json:"page_token"` 36 | OrderList []OrderInfo `json:"order_list"` 37 | } 38 | 39 | type OrderInfo struct { 40 | OrderId string `json:"order_id"` 41 | PricePlanId string `json:"price_plan_id"` 42 | PricePlanType string `json:"price_plan_type"` 43 | Seats int `json:"seats"` 44 | BuyCount int `json:"buy_count"` 45 | CreateTime string `json:"create_time"` 46 | PayTime string `json:"pay_time"` 47 | Status string `json:"status"` 48 | BuyType string `json:"buy_type"` 49 | SrcOrderId string `json:"src_order_id"` 50 | DstOrderId string `json:"dst_order_id"` 51 | OrderPayPrice int64 `json:"order_pay_price"` 52 | TenantKey string `json:"tenant_key"` 53 | } 54 | 55 | type OrderInfoResp struct { 56 | CommonVo 57 | Data OrderInfoData `json:"data"` 58 | } 59 | 60 | type OrderInfoData struct { 61 | Order OrderInfo `json:"order"` 62 | } 63 | -------------------------------------------------------------------------------- /core/model/vo/robot_chat_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type CreateChatReqVo struct { 4 | Name string `json:"name"` 5 | Description string `json:"description"` 6 | OpenIds []string `json:"open_ids"` 7 | UserIds []string `json:"user_ids"` 8 | I18nNames map[string]string `json:"i18n_names"` 9 | } 10 | 11 | type CreateChatRespVo struct { 12 | CommonVo 13 | Data CreateChatData `json:"data"` 14 | } 15 | 16 | type CreateChatData struct { 17 | ChatId string `json:"chat_id"` 18 | InvalidOpenIds []string `json:"invalid_open_ids"` 19 | InvalidUserIds []string `json:"invalid_user_ids"` 20 | } 21 | 22 | type ChatInfoRespVo struct { 23 | CommonVo 24 | Data ChatInfoData `json:"data"` 25 | } 26 | 27 | type ChatInfoData struct { 28 | Avatar string `json:"avatar"` 29 | ChatId string `json:"chat_id"` 30 | Description string `json:"description"` 31 | I18nNames map[string]string `json:"i18n_names"` 32 | Members []ChatMemberData `json:"members"` 33 | Name string `json:"name"` 34 | Type string `json:"type"` 35 | OwnerOpenId string `json:"owner_open_id"` 36 | OwnerUserId string `json:"owner_user_id"` 37 | } 38 | 39 | type ChatMemberData struct { 40 | OpenId string `json:"open_id"` 41 | UserId string `json:"user_id"` 42 | } 43 | 44 | type UpdateChatReqVo struct { 45 | ChatId string `json:"chat_id"` 46 | OwnerUserId string `json:"owner_user_id,omitempty"` 47 | OwnerOpenId string `json:"owner_open_id,omitempty"` 48 | Name string `json:"name,omitempty"` 49 | I18nNames map[string]string `json:"i18n_names,omitempty"` 50 | } 51 | 52 | type UpdateChatRespVo struct { 53 | CommonVo 54 | Data UpdateChatData `json:"data"` 55 | } 56 | 57 | type UpdateChatData struct { 58 | ChatId string `json:"chat_id"` 59 | } 60 | 61 | type UpdateChatMemberReqVo struct { 62 | ChatId string `json:"chat_id"` 63 | UserIds []string `json:"user_ids"` 64 | OpenIds []string `json:"open_ids"` 65 | } 66 | 67 | type UpdateChatMemberRespVo struct { 68 | CommonVo 69 | Data UpdateChatMemberRespData `json:"data"` 70 | } 71 | 72 | type UpdateChatMemberRespData struct { 73 | InvalidOpenIds []string `json:"invalid_open_ids"` 74 | InvalidUserIds []string `json:"invalid_user_ids"` 75 | } 76 | -------------------------------------------------------------------------------- /core/model/vo/robot_message_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | //定义参照: https://open.feishu.cn/open-apis/message/v4/send/ 8 | type MsgVo struct { 9 | OpenId string `json:"open_id,omitempty"` 10 | UserId string `json:"user_id,omitempty"` 11 | Email string `json:"email,omitempty"` 12 | ChatId string `json:"chat_id,omitempty"` 13 | MsgType string `json:"msg_type"` 14 | RootId string `json:"root_id,omitempty"` 15 | UpdateMulti bool `json:"update_multi"` 16 | 17 | Card *Card `json:"card,omitempty"` 18 | Content *MsgContent `json:"content,omitempty"` 19 | } 20 | 21 | type BatchMsgVo struct { 22 | DepartmentIds []string `json:"department_ids"` 23 | OpenIds []string `json:"open_ids"` 24 | UserIds []string `json:"user_ids"` 25 | MsgType string `json:"msg_type"` 26 | 27 | Card *Card `json:"card,omitempty"` 28 | Content *MsgContent `json:"content,omitempty"` 29 | } 30 | 31 | type MsgContent struct { 32 | Text string `json:"text"` 33 | ImageKey string `json:"image_key"` 34 | ShareChatId string `json:"share_chat_id"` 35 | Post *MsgPost `json:"post,omitempty"` 36 | } 37 | 38 | type MsgPost struct { 39 | ZhCn *MsgPostValue `json:"zh_cn,omitempty"` 40 | EnUs *MsgPostValue `json:"en_us,omitempty"` 41 | JaJp *MsgPostValue `json:"ja_jp,omitempty"` 42 | } 43 | 44 | type MsgPostValue struct { 45 | Title string `json:"title"` 46 | Content interface{} `json:"content"` 47 | } 48 | 49 | type MsgPostContentText struct { 50 | Tag string `json:"tag"` 51 | UnEscape bool `json:"un_escape"` 52 | Text string `json:"text"` 53 | } 54 | 55 | type MsgPostContentA struct { 56 | Tag string `json:"tag"` 57 | Text string `json:"text"` 58 | Href string `json:"href"` 59 | } 60 | 61 | type MsgPostContentAt struct { 62 | Tag string `json:"tag"` 63 | UserId string `json:"user_id"` 64 | } 65 | 66 | type MsgPostContentImage struct { 67 | Tag string `json:"tag"` 68 | ImageKey string `json:"image_key"` 69 | Width float64 `json:"width"` 70 | Height float64 `json:"height"` 71 | } 72 | 73 | type MsgResp struct { 74 | CommonVo 75 | 76 | Data MsgRespData `json:"data"` 77 | } 78 | 79 | type MsgRespData struct { 80 | MessageId string `json:"message_id"` 81 | } 82 | 83 | //机器人消息Card字段数据格式定义 84 | type Card struct { 85 | Config *CardConfig `json:"config,omitempty"` 86 | CardLink *CardElementUrl `json:"card_link,omitempty"` 87 | Header *CardHeader `json:"header,omitempty"` 88 | Elements []interface{} `json:"elements"` 89 | I18nElements *I18nElement `json:"i18n_elements,omitempty"` 90 | } 91 | 92 | type CardConfig struct { 93 | WideScreenMode bool `json:"wide_screen_mode"` 94 | EnableForward bool `json:"enable_forward"` 95 | UpdateMulti bool `json:"update_multi"` 96 | } 97 | 98 | type CardHeader struct { 99 | Title *CardHeaderTitle `json:"title,omitempty"` 100 | Template string `json:"template"` 101 | } 102 | 103 | type CardHeaderTitle struct { 104 | Tag string `json:"tag"` 105 | Content string `json:"content"` 106 | Lines int `json:"lines,omitempty"` 107 | I18n *CardI18n `json:"i18n,omitempty"` 108 | } 109 | 110 | type CardI18n struct { 111 | ZhCn string `json:"zh_cn"` 112 | EnUs string `json:"en_us"` 113 | JaJp string `json:"ja_jp"` 114 | } 115 | 116 | type CardElementContentModule struct { 117 | Tag string `json:"tag"` 118 | 119 | Text *CardElementText `json:"text"` 120 | Fields []CardElementField `json:"fields"` 121 | Extra *CardElementExtra `json:"extra,omitempty"` 122 | } 123 | 124 | type CardElementBrModule struct { 125 | Tag string `json:"tag"` 126 | } 127 | 128 | type CardElementImageModule struct { 129 | Tag string `json:"tag"` 130 | 131 | CustomWidth int `json:"custom_width"` 132 | CompactWidth bool `json:"compact_width"` 133 | Mode string `json:"mode"` 134 | Preview bool `json:"preview"` 135 | ImgKey string `json:"img_key"` 136 | Alt CardElementText `json:"alt"` 137 | Title *CardElementText `json:"title,omitempty"` 138 | } 139 | 140 | type CardElementTextAlt struct { 141 | Tag string `json:"tag"` 142 | Content string `json:"content"` 143 | Lines int `json:"lines,omitempty"` 144 | Href map[string]CardElementUrl `json:"href,omitempty"` 145 | } 146 | 147 | type CardElementActionModule struct { 148 | Tag string `json:"tag"` 149 | Layout string `json:"layout"` 150 | Actions []interface{} `json:"actions"` 151 | } 152 | 153 | type ActionButton struct { 154 | Tag string `json:"tag"` 155 | 156 | Text CardElementText `json:"text"` 157 | Url string `json:"url,omitempty"` 158 | MultiUrl *CardElementUrl `json:"multi_url"` 159 | Type string `json:"type,omitempty"` 160 | Value map[string]interface{} `json:"value,omitempty"` 161 | Confirm *CardElementConfirm `json:"confirm,omitempty"` 162 | } 163 | 164 | type ActionSelectMenu struct { 165 | Tag string `json:"tag"` 166 | 167 | Placeholder *CardElementText `json:"placeholder,omitempty"` 168 | InitialOption string `json:"initial_option,omitempty"` 169 | Options []CardElementOption `json:"options"` 170 | Value map[string]interface{} `json:"value,omitempty"` 171 | Confirm *CardElementConfirm `json:"confirm,omitempty"` 172 | } 173 | 174 | type ActionOverflow struct { 175 | Tag string `json:"tag"` 176 | 177 | Options []CardElementOption `json:"options"` 178 | Value map[string]interface{} `json:"value,omitempty"` 179 | Confirm *CardElementConfirm `json:"confirm,omitempty"` 180 | } 181 | 182 | type ActionDatePicker struct { 183 | Tag string `json:"tag"` 184 | 185 | InitialDate string `json:"initial_date,omitempty"` 186 | InitialTime string `json:"initial_time,omitempty"` 187 | InitialDatetime string `json:"initial_datetime,omitempty"` 188 | Placeholder *CardElementText `json:"placeholder,omitempty"` 189 | Value map[string]interface{} `json:"value,omitempty"` 190 | Confirm *CardElementConfirm `json:"confirm,omitempty"` 191 | } 192 | 193 | type CardElementExtra struct { 194 | Tag string `json:"tag"` 195 | ImgKey string `json:"img_key"` 196 | Alt *CardElementText `json:"alt,omitempty"` 197 | } 198 | 199 | type CardElementField struct { 200 | IsShort bool `json:"is_short"` 201 | Text CardElementText `json:"text,omitempty"` 202 | } 203 | 204 | type I18nElement struct { 205 | ZhCn []interface{} `json:"zh_cn"` 206 | EnUs []interface{} `json:"en_us"` 207 | JaJp []interface{} `json:"ja_jp"` 208 | } 209 | 210 | type CardElementText struct { 211 | Tag string `json:"tag"` 212 | Content string `json:"content"` 213 | Lines int `json:"lines,omitempty"` 214 | Href map[string]CardElementUrl `json:"href,omitempty"` 215 | } 216 | 217 | type CardElementAction struct { 218 | Tag string `json:"tag"` 219 | Text *CardElementText `json:"text,omitempty"` 220 | Type string `json:"type"` 221 | } 222 | 223 | type CardElementUrl struct { 224 | Url string `json:"url"` 225 | AndroidUrl string `json:"android_url"` 226 | IosUrl string `json:"ios_url"` 227 | PcUrl string `json:"pc_url"` 228 | } 229 | 230 | type CardElementConfirm struct { 231 | Title *CardHeaderTitle `json:"title,omitempty"` 232 | Text *CardElementText `json:"text,omitempty"` 233 | } 234 | 235 | type CardElementOption struct { 236 | Text *CardElementText `json:"text,omitempty"` 237 | Value string `json:"value"` 238 | Url string `json:"url,omitempty"` 239 | MultiUrl *CardElementUrl `json:"multi_url,omitempty"` 240 | } 241 | 242 | type UpdateImageVo struct { 243 | Image binary.ByteOrder 244 | } 245 | 246 | type CardElementNote struct { 247 | Tag string `json:"tag"` 248 | Elements []interface{} `json:"elements"` 249 | } 250 | -------------------------------------------------------------------------------- /core/model/vo/role_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type RoleListResp struct { 4 | CommonVo 5 | Data RoleListRespData `json:"data"` 6 | } 7 | 8 | type RoleListRespData struct { 9 | RoleList []Role `json:"role_list"` 10 | } 11 | 12 | type Role struct { 13 | Id string `json:"id"` 14 | Name string `json:"name"` 15 | } 16 | 17 | type RoleMemberListResp struct { 18 | CommonVo 19 | Data RoleMemberListRespData `json:"data"` 20 | } 21 | 22 | type RoleMemberListRespData struct { 23 | HasMore bool `json:"has_more"` 24 | PageToken string `json:"page_token"` 25 | UserList []UserRestInfoVo `json:"user_list"` 26 | } -------------------------------------------------------------------------------- /core/model/vo/scopes_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type GetScopesResp struct { 4 | CommonVo 5 | Data GetScopesRespData `json:"data"` 6 | } 7 | 8 | type GetScopesRespData struct { 9 | Scopes []GetScopesRespDataScopes `json:"scopes"` 10 | } 11 | 12 | type GetScopesRespDataScopes struct { 13 | ScopeName string `json:"scope_name"` 14 | GrantStatus int `json:"grant_status"` 15 | } 16 | 17 | type ApplyScopesResp struct { 18 | CommonVo 19 | Data ApplyScopesRespData `json:"data"` 20 | } 21 | 22 | type ApplyScopesRespData struct { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/model/vo/user_vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type GetDepartmentUserListRespVo struct { 4 | CommonVo 5 | Data *GetDepartmentUserListRespVoData `json:"data"` 6 | } 7 | 8 | type GetDepartmentUserListV2RespVo struct { 9 | CommonVo 10 | Data *GetDepartmentUserListV2RespVoData `json:"data"` 11 | } 12 | 13 | type GetDepartmentUserDetailListRespVo struct { 14 | CommonVo 15 | Data *GetDepartmentUserDetailListRespVoData `json:"data"` 16 | } 17 | 18 | type GetDepartmentUserDetailListRespVoData struct { 19 | HasMore bool `json:"has_more"` 20 | UserInfos []UserDetailInfo `json:"user_infos"` 21 | } 22 | 23 | type GetDepartmentUserListRespVoData struct { 24 | HasMore bool `json:"has_more"` 25 | UserList []UserRestInfoVo `json:"user_list"` 26 | } 27 | 28 | type GetDepartmentUserListV2RespVoData struct { 29 | HasMore bool `json:"has_more"` 30 | PageToken string `json:"page_token"` 31 | UserList []UserRestInfoVo `json:"users"` 32 | } 33 | 34 | type UserRestInfoVo struct { 35 | EmployeeId string `json:"employee_id"` 36 | OpenId string `json:"open_id"` 37 | UserId string `json:"user_id"` 38 | Name string `json:"name"` 39 | EmployeeNo string `json:"employee_no"` 40 | } 41 | 42 | type GetDepartmentSimpleListRespVo struct { 43 | CommonVo 44 | Data *GetDepartmentSimpleListRespVoData `json:"data"` 45 | } 46 | 47 | type GetDepartmentSimpleListV2RespVo struct { 48 | CommonVo 49 | Data *GetDepartmentSimpleListV2RespVoData `json:"data"` 50 | } 51 | 52 | type GetScopeRespVo struct { 53 | CommonVo 54 | Data *GetScopeRespData `json:"data"` 55 | } 56 | 57 | type GetScopeRespV2Vo struct { 58 | CommonVo 59 | Data *GetScopeRespV2Data `json:"data"` 60 | } 61 | 62 | type GetScopeRespData struct { 63 | AuthedDepartments []string `json:"authed_departments"` 64 | AuthedEmployeeIds []string `json:"authed_employee_ids"` 65 | AuthedOpenIds []string `json:"authed_open_ids"` 66 | } 67 | 68 | type GetScopeRespV2Data struct { 69 | AuthedDepartments []string `json:"authed_departments"` 70 | AuthedUsers []ScopeUser `json:"authed_users"` 71 | } 72 | 73 | type ScopeUser struct { 74 | OpenId string `json:"open_id"` 75 | UserId string `json:"user_id"` 76 | } 77 | 78 | type GetDepartmentSimpleListRespVoData struct { 79 | HasMore bool `json:"has_more"` 80 | PageToken string `json:"page_token"` 81 | DepartmentInfos []DepartmentRestInfoVo `json:"department_infos"` 82 | } 83 | 84 | type GetDepartmentSimpleListV2RespVoData struct { 85 | HasMore bool `json:"has_more"` 86 | PageToken string `json:"page_token"` 87 | DepartmentInfos []DepartmentRestInfoVo `json:"departments"` 88 | } 89 | 90 | type DepartmentRestInfoVo struct { 91 | Id string `json:"id"` 92 | Name string `json:"name"` 93 | ParentId string `json:"parent_id"` 94 | OpenDepartmentID string `json:"open_department_id"` 95 | ParentOpenDepartmentID string `json:"parent_open_department_id"` 96 | } 97 | 98 | type GetDepartmentInfoRespVo struct { 99 | CommonVo 100 | Data *GetDepartmentInfoRespVoData `json:"data"` 101 | } 102 | 103 | type GetDepartmentInfoBatchRespVo struct { 104 | CommonVo 105 | Data GetDepartmentInfoBatchRespData `json:"data"` 106 | } 107 | 108 | type GetDepartmentInfoBatchRespData struct { 109 | Departments []DepartmentDetailInfo `json:"departments"` 110 | Errors []GetUserBatchError `json:"errors"` 111 | } 112 | 113 | type DepartmentDetailInfo struct { 114 | ChatId string `json:"chat_id"` 115 | HasChild bool `json:"has_child"` 116 | Id string `json:"id"` 117 | Leader Leader `json:"leader"` 118 | MemberCount int `json:"member_count"` 119 | Name string `json:"name"` 120 | ParentId string `json:"parent_id"` 121 | Status int `json:"status"` 122 | } 123 | 124 | type GetDepartmentInfoRespVoData struct { 125 | DepartmentInfo *DepartmentDetailInfoVo `json:"department_info"` 126 | } 127 | 128 | type DepartmentDetailInfoVo struct { 129 | Id string `json:"id"` 130 | LeaderEmployeeId string `json:"leader_employee_id"` 131 | LeaderOpenId string `json:"leader_open_id"` 132 | ChatId string `json:"chat_id"` 133 | MemberCount int `json:"member_count"` 134 | Name string `json:"name"` 135 | ParentId string `json:"parent_id"` 136 | Status int `json:"status"` 137 | } 138 | 139 | type GetUserBatchGetRespVo struct { 140 | CommonVo 141 | Data *GetUserBatchGetRespVoData `json:"data"` 142 | } 143 | 144 | type GetUserBatchGetRespVoData struct { 145 | UserInfos []UserDetailInfo `json:"user_infos"` 146 | } 147 | 148 | type UserDetailInfo struct { 149 | Name string `json:"name"` 150 | NamePy string `json:"name_py"` 151 | EnName string `json:"en_name"` 152 | EmployeeId string `json:"employee_id"` 153 | EmployeeNo string `json:"employee_no"` 154 | OpenId string `json:"open_id"` 155 | Status int `json:"status"` 156 | EmployeeType int `json:"employee_type"` 157 | Avatar71 string `json:"avatar_71"` 158 | Avatar240 string `json:"avatar_240"` 159 | Avatar640 string `json:"avatar_640"` 160 | AvatarUrl string `json:"avatar_url"` 161 | Gender int `json:"gender"` 162 | Email string `json:"email"` 163 | Mobile string `json:"mobile"` 164 | Description string `json:"description"` 165 | Country string `json:"country"` 166 | City string `json:"city"` 167 | WorkStation string `json:"work_station"` 168 | IsTenantManager bool `json:"is_tenant_manager"` 169 | JoinTime int64 `json:"join_time"` 170 | UpdateTime int64 `json:"update_time"` 171 | LeaderEmployeeId string `json:"leader_employee_id"` 172 | LeaderOpenId string `json:"leader_open_id"` 173 | Departments []string `json:"departments"` 174 | CustomAttr map[string]Entry `json:"custom_attr"` 175 | } 176 | 177 | type UserDetailInfoV2 struct { 178 | Name string `json:"name"` 179 | NamePy string `json:"name_py"` 180 | EnName string `json:"en_name"` 181 | EmployeeId string `json:"employee_id"` 182 | EmployeeNo string `json:"employee_no"` 183 | OpenId string `json:"open_id"` 184 | UserId string `json:"user_id"` 185 | UnionId string `json:"union_id"` 186 | Status UserStatus `json:"status"` 187 | EmployeeType int `json:"employee_type"` 188 | Avatar UserAvatar `json:"avatar"` 189 | Gender int `json:"gender"` 190 | Email string `json:"email"` 191 | Mobile string `json:"mobile"` 192 | Description string `json:"description"` 193 | Country string `json:"country"` 194 | City string `json:"city"` 195 | WorkStation string `json:"work_station"` 196 | IsTenantManager bool `json:"is_tenant_manager"` 197 | JoinTime int64 `json:"join_time"` 198 | UpdateTime int64 `json:"update_time"` 199 | Leader Leader `json:"leader"` 200 | Departments []string `json:"departments"` 201 | Positions []Position `json:"positions"` 202 | Orders []Order `json:"orders"` 203 | CustomAttrs []CustomAttr `json:"custom_attrs"` 204 | } 205 | 206 | type UserDetailInfoV3 struct { 207 | Name string `json:"name"` 208 | NamePy string `json:"name_py"` 209 | EnName string `json:"en_name"` 210 | EmployeeId string `json:"employee_id"` 211 | EmployeeNo string `json:"employee_no"` 212 | OpenId string `json:"open_id"` 213 | UserId string `json:"user_id"` 214 | UnionId string `json:"union_id"` 215 | Status UserStatus `json:"status"` 216 | EmployeeType int `json:"employee_type"` 217 | Avatar UserAvatar `json:"avatar"` 218 | Gender int `json:"gender"` 219 | Email string `json:"email"` 220 | Mobile string `json:"mobile"` 221 | Description string `json:"description"` 222 | Country string `json:"country"` 223 | City string `json:"city"` 224 | WorkStation string `json:"work_station"` 225 | IsTenantManager bool `json:"is_tenant_manager"` 226 | JoinTime int64 `json:"join_time"` 227 | UpdateTime int64 `json:"update_time"` 228 | LeaderUserId string `json:"leader_user_id"` 229 | DepartmentIds []string `json:"department_ids"` 230 | Positions []Position `json:"positions"` 231 | Orders []Order `json:"orders"` 232 | CustomAttrs []CustomAttr `json:"custom_attrs"` 233 | } 234 | 235 | type CustomAttr struct { 236 | Id string `json:"id"` 237 | Type string `json:"type"` 238 | Value CustomAttrValue `json:"value"` 239 | } 240 | 241 | type CustomAttrValue struct { 242 | Url string `json:"url"` 243 | PcUrl string `json:"pc_url"` 244 | } 245 | 246 | type Order struct { 247 | DepartmentId string `json:"department_id"` 248 | UserOrder int `json:"user_order"` 249 | DepartmentOrder int `json:"department_order"` 250 | } 251 | 252 | type Position struct { 253 | PositionCode string `json:"position_code"` 254 | PositionName string `json:"position_name"` 255 | DepartmentId string `json:"department_id"` 256 | Leader Leader `json:"leader"` 257 | IsMajor bool `json:"is_major"` 258 | } 259 | 260 | type Leader struct { 261 | OpenId string `json:"open_id"` 262 | UserId string `json:"user_id"` 263 | PositionCode string `json:"position_code"` 264 | } 265 | 266 | type UserStatus struct { 267 | IsFrozen bool `json:"is_frozen"` 268 | IsResigned bool `json:"is_resigned"` 269 | IsActivated bool `json:"is_activated"` 270 | } 271 | 272 | type Entry struct { 273 | Value string `json:"value"` 274 | } 275 | 276 | type SearchUserResp struct { 277 | CommonVo 278 | Data *SearchUserRespData `json:"data"` 279 | } 280 | 281 | type SearchUserRespData struct { 282 | HasMore bool `json:"has_more"` 283 | PageToken string `json:"page_token"` 284 | Users []SearchUserInfo `json:"users"` 285 | } 286 | 287 | type SearchUserInfo struct { 288 | Avatar UserAvatar `json:"avatar"` 289 | DepartmentIds []string `json:"department_ids"` 290 | Name string `json:"name"` 291 | OpenId string `json:"open_id"` 292 | UserId string `json:"user_id"` 293 | } 294 | 295 | type UserAvatar struct { 296 | Avatar72 string `json:"avatar_72"` 297 | Avatar240 string `json:"avatar_240"` 298 | Avatar640 string `json:"avatar_640"` 299 | AvatarOrigin string `json:"avatar_origin"` 300 | } 301 | 302 | type GetUserBatchGetV2Resp struct { 303 | CommonVo 304 | Data GetUserBatchGetV2RespData `json:"data"` 305 | } 306 | 307 | type GetUserBatchGetV2RespData struct { 308 | HasMore bool `json:"has_more"` 309 | PageToken string `json:"page_token"` 310 | Users []UserDetailInfoV2 `json:"users"` 311 | Errors []GetUserBatchError `json:"errors"` 312 | } 313 | 314 | type GetUserBatchError struct { 315 | Id string `json:"id"` 316 | Code int `json:"code"` 317 | Msg string `json:"msg"` 318 | } 319 | 320 | type GetUsersV3Resp struct { 321 | CommonVo 322 | Data GetUsersV3RespData `json:"data"` 323 | } 324 | 325 | type GetUsersV3RespData struct { 326 | HasMore bool `json:"has_more"` 327 | PageToken string `json:"page_token"` 328 | Items []UserDetailInfoV3 `json:"items"` 329 | } 330 | 331 | type BatchGetIdResp struct { 332 | CommonVo 333 | Data BatchGetIdData `json:"data"` 334 | } 335 | 336 | type BatchGetIdData struct { 337 | EmailUsers map[string][]SimpleIdInfo `json:"email_users"` 338 | EmailsNotExist []string `json:"emails_not_exist"` 339 | MobileUsers map[string][]SimpleIdInfo `json:"mobile_users"` 340 | MobilesNotExist []string `json:"mobiles_not_exist"` 341 | } 342 | 343 | type SimpleIdInfo struct { 344 | OpenId string `json:"open_Id"` 345 | UserId string `json:"user_id"` 346 | } 347 | 348 | type AuthenAccessTokenResp struct { 349 | CommonVo 350 | Data AuthenAccessTokenData `json:"data"` 351 | } 352 | 353 | type AuthenAccessTokenData struct { 354 | AccessToken string `json:"access_token"` 355 | TokenType string `json:"token_type"` 356 | ExpiresIn int64 `json:"expires_in"` 357 | Name string `json:"name"` 358 | EnName string `json:"en_name"` 359 | AvatarUrl string `json:"avatar_url"` 360 | AvatarThumb string `json:"avatar_thumb"` 361 | AvatarMiddle string `json:"avatar_middle"` 362 | AvatarBig string `json:"avatar_big"` 363 | OpenId string `json:"open_id"` 364 | Email string `json:"email"` 365 | UserId string `json:"user_id"` 366 | Mobile string `json:"mobile"` 367 | TenantKey string `json:"tenant_key"` 368 | RefreshExpiresIn int64 `json:"refresh_expires_in"` 369 | RefreshToken string `json:"refresh_token"` 370 | } 371 | -------------------------------------------------------------------------------- /core/model/vo/vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type CommonVo struct { 4 | Code int `json:"code"` 5 | Msg string `json:"msg"` 6 | } -------------------------------------------------------------------------------- /core/util/encrypt/aes.go: -------------------------------------------------------------------------------- 1 | package encrypt 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "encoding/base64" 7 | "errors" 8 | ) 9 | 10 | func AesDecrypt(key string, encrypt string) (string, error){ 11 | kbs := SHA256(key) 12 | decode, err := base64.StdEncoding.DecodeString(encrypt) 13 | if err != nil { 14 | return "", err 15 | } 16 | if len(decode) < aes.BlockSize { 17 | return "", errors.New("密文太短啦") 18 | } 19 | iv := decode[:aes.BlockSize] 20 | block, err := aes.NewCipher(kbs) 21 | if err != nil{ 22 | return "", err 23 | } 24 | blockMode := cipher.NewCBCDecrypter(block, iv) 25 | plantText := make([]byte, len(decode)) 26 | blockMode.CryptBlocks(plantText, decode) 27 | plantText = PKCS7UnPadding(plantText) 28 | plantText = plantText[aes.BlockSize:] 29 | return string(plantText), nil 30 | } 31 | 32 | func PKCS7UnPadding(plantText []byte) []byte { 33 | length := len(plantText) 34 | unpadding := int(plantText[length-1]) 35 | return plantText[:(length - unpadding)] 36 | } -------------------------------------------------------------------------------- /core/util/encrypt/aes_test.go: -------------------------------------------------------------------------------- 1 | package encrypt 2 | 3 | import ( 4 | "gotest.tools/assert" 5 | "testing" 6 | ) 7 | 8 | func TestAesDecrypt(t *testing.T) { 9 | 10 | key := "test key" 11 | encrypt := "P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk=" 12 | 13 | d, e := AesDecrypt(key, encrypt) 14 | t.Log(e) 15 | t.Log(d) 16 | 17 | assert.Equal(t, d, "hello world") 18 | } -------------------------------------------------------------------------------- /core/util/encrypt/encoder.go: -------------------------------------------------------------------------------- 1 | package encrypt 2 | import ( 3 | "encoding/base64" 4 | "net/url" 5 | ) 6 | 7 | func BASE64(input []byte) string{ 8 | return base64.StdEncoding.EncodeToString(input) 9 | } 10 | 11 | func URLEncode(input string) string{ 12 | return url.QueryEscape(input) 13 | } -------------------------------------------------------------------------------- /core/util/encrypt/hmac.go: -------------------------------------------------------------------------------- 1 | package encrypt 2 | 3 | import ( 4 | "crypto/sha1" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | ) 8 | 9 | func SHA256(source string) []byte { 10 | mac := sha256.New() 11 | mac.Write([]byte(source)) 12 | return mac.Sum(nil) 13 | } 14 | 15 | func SHA1(source string) string { 16 | sha1 := sha1.New() 17 | sha1.Write([]byte(source)) 18 | return hex.EncodeToString(sha1.Sum([]byte(nil))) 19 | } 20 | -------------------------------------------------------------------------------- /core/util/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "mime/multipart" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func GetFileReader(path string) (*bytes.Buffer, error) { 12 | bodyBuf := &bytes.Buffer{} 13 | bodyWriter := multipart.NewWriter(bodyBuf) 14 | 15 | fileWriter, err := bodyWriter.CreateFormFile("media", filepath.Base(path)) 16 | if err != nil { 17 | return nil, err 18 | } 19 | fh, err := os.Open(path) 20 | if err != nil { 21 | return nil, err 22 | } 23 | defer fh.Close() 24 | _, _ = io.Copy(fileWriter, fh) 25 | 26 | bodyWriter.Close() 27 | return bodyBuf, err 28 | } 29 | -------------------------------------------------------------------------------- /core/util/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "fmt" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | "io/ioutil" 10 | "net/http" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | const defaultContentType = "application/json" 16 | 17 | var httpClient = &http.Client{} 18 | 19 | type HeaderOption struct { 20 | Name string 21 | Value string 22 | } 23 | 24 | type QueryParameter struct { 25 | Key string 26 | Value interface{} 27 | } 28 | 29 | func init() { 30 | tr := &http.Transport{ 31 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 32 | } 33 | httpClient = &http.Client{ 34 | Transport: tr, 35 | Timeout: time.Duration(30) * time.Second, 36 | } 37 | } 38 | 39 | func BuildTokenHeaderOptions(tenantAccessToken string) HeaderOption { 40 | return HeaderOption{ 41 | Name: "Authorization", 42 | Value: "Bearer " + tenantAccessToken, 43 | } 44 | } 45 | 46 | func DeleteRequest(url string, body string, headerOptions ...HeaderOption) (string, error) { 47 | req, err := http.NewRequest("DELETE", url, strings.NewReader(body)) 48 | if err != nil { 49 | return "", err 50 | } 51 | req.Header.Set("Content-Type", defaultContentType) 52 | for _, headerOption := range headerOptions { 53 | req.Header.Set(headerOption.Name, headerOption.Value) 54 | } 55 | resp, err := httpClient.Do(req) 56 | defer func() { 57 | if resp != nil { 58 | if e := resp.Body.Close(); e != nil { 59 | fmt.Println(e) 60 | } 61 | } 62 | }() 63 | return responseHandle(resp, err) 64 | } 65 | 66 | func Delete(url string, params map[string]interface{}, body string, headerOptions ...HeaderOption) (string, error) { 67 | log.InfoF("请求body %s", body) 68 | 69 | fullUrl := url + ConvertToQueryParams(params) 70 | return DeleteRequest(fullUrl, body, headerOptions...) 71 | } 72 | 73 | func PatchRequest(url string, body string, headerOptions ...HeaderOption) (string, error) { 74 | req, err := http.NewRequest("PATCH", url, strings.NewReader(body)) 75 | if err != nil { 76 | return "", err 77 | } 78 | req.Header.Set("Content-Type", defaultContentType) 79 | for _, headerOption := range headerOptions { 80 | req.Header.Set(headerOption.Name, headerOption.Value) 81 | } 82 | resp, err := httpClient.Do(req) 83 | defer func() { 84 | if resp != nil { 85 | if e := resp.Body.Close(); e != nil { 86 | fmt.Println(e) 87 | } 88 | } 89 | }() 90 | return responseHandle(resp, err) 91 | } 92 | 93 | func Patch(url string, params map[string]interface{}, body string, headerOptions ...HeaderOption) (string, error) { 94 | log.InfoF("请求body %s", body) 95 | 96 | fullUrl := url + ConvertToQueryParams(params) 97 | return PatchRequest(fullUrl, body, headerOptions...) 98 | } 99 | 100 | func PostRequest(url string, body string, headerOptions ...HeaderOption) (string, error) { 101 | req, err := http.NewRequest("POST", url, strings.NewReader(body)) 102 | if err != nil { 103 | return "", err 104 | } 105 | req.Header.Set("Content-Type", defaultContentType) 106 | for _, headerOption := range headerOptions { 107 | req.Header.Set(headerOption.Name, headerOption.Value) 108 | } 109 | resp, err := httpClient.Do(req) 110 | defer func() { 111 | if resp != nil { 112 | if e := resp.Body.Close(); e != nil { 113 | fmt.Println(e) 114 | } 115 | } 116 | }() 117 | return responseHandle(resp, err) 118 | } 119 | 120 | func Post(url string, params map[string]interface{}, body string, headerOptions ...HeaderOption) (string, error) { 121 | log.InfoF("请求body %s", body) 122 | 123 | fullUrl := url + ConvertToQueryParams(params) 124 | return PostRequest(fullUrl, body, headerOptions...) 125 | } 126 | 127 | func PostRepetition(url string, params []QueryParameter, body string, headerOptions ...HeaderOption) (string, error) { 128 | log.InfoF("请求body %s", body) 129 | 130 | fullUrl := url + ConvertToQueryParamsRepetition(params) 131 | return PostRequest(fullUrl, body, headerOptions...) 132 | } 133 | 134 | func GetRequest(url string, headerOptions ...HeaderOption) (string, error) { 135 | req, err := http.NewRequest("GET", url, nil) 136 | if err != nil { 137 | return "", err 138 | } 139 | for _, headerOption := range headerOptions { 140 | req.Header.Set(headerOption.Name, headerOption.Value) 141 | } 142 | resp, err := httpClient.Do(req) 143 | defer func() { 144 | if resp != nil { 145 | if e := resp.Body.Close(); e != nil { 146 | fmt.Println(e) 147 | } 148 | } 149 | }() 150 | return responseHandle(resp, err) 151 | } 152 | 153 | func Get(url string, params map[string]interface{}, headerOptions ...HeaderOption) (string, error) { 154 | fullUrl := url + ConvertToQueryParams(params) 155 | return GetRequest(fullUrl, headerOptions...) 156 | } 157 | 158 | func GetRepetition(url string, params []QueryParameter, headerOptions ...HeaderOption) (string, error) { 159 | fullUrl := url + ConvertToQueryParamsRepetition(params) 160 | return GetRequest(fullUrl, headerOptions...) 161 | } 162 | 163 | func responseHandle(resp *http.Response, err error) (string, error) { 164 | if err != nil { 165 | log.Error(err) 166 | return "", err 167 | } 168 | b, err := ioutil.ReadAll(resp.Body) 169 | if err != nil { 170 | log.Error(err) 171 | return "", err 172 | } 173 | respBody := string(b) 174 | log.InfoF("api %s 响应结果: %s", resp.Request.URL, respBody) 175 | return respBody, nil 176 | } 177 | 178 | func ConvertToQueryParams(params map[string]interface{}) string { 179 | paramsJson := json.ToJsonIgnoreError(params) 180 | params = map[string]interface{}{} 181 | _ = json.FromJson(paramsJson, ¶ms) 182 | 183 | if ¶ms == nil || len(params) == 0 { 184 | return "" 185 | } 186 | var buffer bytes.Buffer 187 | buffer.WriteString("?") 188 | for k, v := range params { 189 | if v == nil { 190 | continue 191 | } 192 | buffer.WriteString(fmt.Sprintf("%s=%v&", k, v)) 193 | } 194 | buffer.Truncate(buffer.Len() - 1) 195 | return buffer.String() 196 | } 197 | 198 | func ConvertToQueryParamsRepetition(params []QueryParameter) string { 199 | var buffer bytes.Buffer 200 | buffer.WriteString("?") 201 | for _, v := range params { 202 | if v.Value == nil { 203 | continue 204 | } 205 | buffer.WriteString(fmt.Sprintf("%s=%v&", v.Key, v.Value)) 206 | } 207 | buffer.Truncate(buffer.Len() - 1) 208 | return buffer.String() 209 | } 210 | -------------------------------------------------------------------------------- /core/util/http/http_test.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "gotest.tools/assert" 7 | "testing" 8 | ) 9 | 10 | func TestConvertToQueryParams(t *testing.T) { 11 | str := ConvertToQueryParams(map[string]interface{}{ 12 | "a": "aa", 13 | "c": nil, 14 | }) 15 | assert.Equal(t, str, "?a=aa") 16 | } 17 | 18 | func TestConvertToQueryParams2(t *testing.T) { 19 | 20 | t.Log(json.ToJsonIgnoreError(vo.UpdateChatReqVo{ 21 | ChatId: "111", 22 | OwnerOpenId: "111", 23 | })) 24 | } 25 | -------------------------------------------------------------------------------- /core/util/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | jsoniter "github.com/json-iterator/go" 5 | ) 6 | 7 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 8 | 9 | func ToJson(obj interface{}) (string, error) { 10 | bs, err := json.Marshal(obj) 11 | if err != nil { 12 | return "", err 13 | } 14 | return string(bs), nil 15 | } 16 | 17 | func ToJsonIgnoreError(obj interface{}) string { 18 | jsonStr, _ := ToJson(obj) 19 | return jsonStr 20 | } 21 | 22 | func FromJson(jsonStr string, obj interface{}) error { 23 | return json.Unmarshal([]byte(jsonStr), obj) 24 | } 25 | 26 | func FromJsonIgnoreError(jsonStr string, obj interface{}) { 27 | _ = FromJson(jsonStr, obj) 28 | } -------------------------------------------------------------------------------- /core/util/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func Error(msg ...interface{}) { 8 | newMsg := make([]interface{}, 1) 9 | newMsg[0] = "Error ::\t" 10 | newMsg = append(newMsg, msg...) 11 | log.Println(newMsg...) 12 | } 13 | 14 | func Info(msg ...interface{}) { 15 | newMsg := make([]interface{}, 1) 16 | newMsg[0] = "Info ::\t" 17 | newMsg = append(newMsg, msg...) 18 | log.Println(newMsg...) 19 | } 20 | 21 | func ErrorF(msg string, args ...interface{}) { 22 | log.Printf("Error::\t"+msg+"\n", args...) 23 | } 24 | 25 | func InfoF(msg string, args ...interface{}) { 26 | log.Printf("Info::\t"+msg+"\n", args...) 27 | } 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/galaxy-book/feishu-sdk-golang 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/aliyun/aliyun-oss-go-sdk v2.1.5+incompatible 7 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect 8 | github.com/google/go-cmp v0.3.1 // indirect 9 | github.com/json-iterator/go v1.1.10 10 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 11 | github.com/pkg/errors v0.8.1 // indirect 12 | github.com/satori/go.uuid v1.2.0 13 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect 14 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect 15 | gotest.tools v2.2.0+incompatible 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aliyun/aliyun-oss-go-sdk v2.1.5+incompatible h1:v5yDfjkRY/kOxu05gkh0/D/2wYxbTFCoTr3JqFI0FLE= 2 | github.com/aliyun/aliyun-oss-go-sdk v2.1.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= 3 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= 4 | github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= 9 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 10 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 11 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 12 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 13 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 14 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 15 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 16 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 17 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 18 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 19 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 20 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 21 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 22 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 23 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 24 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 25 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 26 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 27 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 28 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 29 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 30 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 31 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= 32 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 33 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 34 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 36 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 37 | -------------------------------------------------------------------------------- /sdk/app.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //校验应用管理员 https://open.feishu.cn/document/ukTMukTMukTM/uITN1EjLyUTNx4iM1UTM 12 | func (t Tenant) IsUserAdmin(openId string, employeeId string) (*vo.IsUserAdminResp, error) { 13 | queryParams := map[string]interface{}{} 14 | if openId != "" { 15 | queryParams["open_id"] = openId 16 | } 17 | if employeeId != "" { 18 | queryParams["employee_id"] = employeeId 19 | } 20 | respBody, err := http.Get(consts.ApiIsUserAdmin, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 21 | if err != nil { 22 | log.Error(err) 23 | return nil, err 24 | } 25 | respVo := &vo.IsUserAdminResp{} 26 | json.FromJsonIgnoreError(respBody, respVo) 27 | return respVo, nil 28 | } 29 | 30 | //查询应用管理员列表 https://open.feishu.cn/document/ukTMukTMukTM/ucDOwYjL3gDM24yN4AjN 31 | func (t Tenant) AdminUserList() (*vo.AdminUserListResp, error) { 32 | respBody, err := http.Get(consts.ApiAdminUserList, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 33 | if err != nil { 34 | log.Error(err) 35 | return nil, err 36 | } 37 | respVo := &vo.AdminUserListResp{} 38 | json.FromJsonIgnoreError(respBody, respVo) 39 | return respVo, nil 40 | } 41 | 42 | //获取企业信息 https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/tenant-v2/tenant/query 43 | func (t Tenant) OrgInfo() (*vo.OrgInfoResp, error) { 44 | respBody, err := http.Get(consts.ApiOrgInfo, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 45 | if err != nil { 46 | log.Error(err) 47 | return nil, err 48 | } 49 | respVo := &vo.OrgInfoResp{} 50 | json.FromJsonIgnoreError(respBody, respVo) 51 | return respVo, nil 52 | } 53 | 54 | func (t Tenant) GetJsTicket() (*vo.GetJsTicketResp, error) { 55 | respBody, err := http.Post(consts.ApiJSApiTicket, nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 56 | if err != nil { 57 | log.Error(err) 58 | return nil, err 59 | } 60 | respVo := &vo.GetJsTicketResp{} 61 | json.FromJsonIgnoreError(respBody, respVo) 62 | return respVo, nil 63 | } 64 | -------------------------------------------------------------------------------- /sdk/app_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "testing" 7 | ) 8 | 9 | func TestTenant_IsUserAdmin(t *testing.T) { 10 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 11 | t.Log(e) 12 | t.Log(json.ToJsonIgnoreError(app)) 13 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 14 | t.Log(e) 15 | 16 | resp, err := tenant.IsUserAdmin("ou_b7d861c94cea4d316a6bfc5e8421994c", "") 17 | t.Log(err) 18 | t.Log(json.ToJsonIgnoreError(resp)) 19 | } 20 | 21 | func TestTenant_AdminUserList(t *testing.T) { 22 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 23 | t.Log(e) 24 | t.Log(json.ToJsonIgnoreError(app)) 25 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 26 | t.Log(e) 27 | 28 | resp, err := tenant.AdminUserList() 29 | t.Log(err) 30 | t.Log(json.ToJsonIgnoreError(resp)) 31 | } 32 | 33 | func TestTenant_OrgInfo(t *testing.T) { 34 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 35 | t.Log(e) 36 | t.Log(json.ToJsonIgnoreError(app)) 37 | tenant, e := BuildTenant(app.AppAccessToken, "116542e77eced75d") 38 | t.Log(e) 39 | 40 | resp, err := tenant.OrgInfo() 41 | t.Log(err) 42 | t.Log(json.ToJsonIgnoreError(resp)) 43 | } 44 | 45 | func TestTenant_GetJsTicket(t *testing.T) { 46 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 47 | t.Log(e) 48 | t.Log(json.ToJsonIgnoreError(app)) 49 | tenant, e := BuildTenant(app.AppAccessToken, "116542e77eced75d") 50 | t.Log(e) 51 | 52 | resp, err := tenant.GetJsTicket() 53 | t.Log(err) 54 | t.Log(json.ToJsonIgnoreError(resp)) 55 | } 56 | -------------------------------------------------------------------------------- /sdk/auth.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //获取 app_access_token(企业自建应用)https://open.feishu.cn/document/ukTMukTMukTM/uADN14CM0UjLwQTN 12 | func GetAppAccessTokenInternal(appId, appSecret string) (*vo.AppAccessTokenInternalRespVo, error) { 13 | reqBody := map[string]interface{}{ 14 | "app_id": appId, 15 | "app_secret": appSecret, 16 | } 17 | respBody, err := http.Post(consts.ApiAppAccessTokenInternal, nil, json.ToJsonIgnoreError(reqBody)) 18 | if err != nil { 19 | log.Error(err) 20 | return nil, err 21 | } 22 | respVo := &vo.AppAccessTokenInternalRespVo{} 23 | json.FromJsonIgnoreError(respBody, respVo) 24 | return respVo, nil 25 | } 26 | 27 | //获取 app_access_token(应用商店应用)https://open.feishu.cn/document/ukTMukTMukTM/uEjNz4SM2MjLxYzM 28 | func GetAppAccessToken(appId, appSecret, appTicket string) (*vo.AppAccessTokenRespVo, error) { 29 | reqBody := map[string]interface{}{ 30 | "app_id": appId, 31 | "app_secret": appSecret, 32 | "app_ticket": appTicket, 33 | } 34 | respBody, err := http.Post(consts.ApiAppAccessToken, nil, json.ToJsonIgnoreError(reqBody)) 35 | if err != nil { 36 | log.Error(err) 37 | return nil, err 38 | } 39 | respVo := &vo.AppAccessTokenRespVo{} 40 | json.FromJsonIgnoreError(respBody, respVo) 41 | return respVo, nil 42 | } 43 | 44 | //获取 tenant_access_token(企业自建应用)https://open.feishu.cn/document/ukTMukTMukTM/uIjNz4iM2MjLyYzM 45 | func GetTenantAccessTokenInternal(appId string, appSecret string) (*vo.TenantAccessTokenRespVo, error) { 46 | reqBody := map[string]interface{}{ 47 | "app_id": appId, 48 | "app_secret": appSecret, 49 | } 50 | respBody, err := http.Post(consts.ApiTenantAccessTokenInternal, nil, json.ToJsonIgnoreError(reqBody)) 51 | if err != nil { 52 | log.Error(err) 53 | return nil, err 54 | } 55 | respVo := &vo.TenantAccessTokenRespVo{} 56 | json.FromJsonIgnoreError(respBody, respVo) 57 | return respVo, nil 58 | } 59 | 60 | //获取 tenant_access_token(应用商店应用)https://open.feishu.cn/document/ukTMukTMukTM/uMjNz4yM2MjLzYzM 61 | func GetTenantAccessToken(appAccessToken string, tenantKey string) (*vo.TenantAccessTokenRespVo, error) { 62 | reqBody := map[string]interface{}{ 63 | "app_access_token": appAccessToken, 64 | "tenant_key": tenantKey, 65 | } 66 | respBody, err := http.Post(consts.ApiTenantAccessToken, nil, json.ToJsonIgnoreError(reqBody)) 67 | if err != nil { 68 | log.Error(err) 69 | return nil, err 70 | } 71 | respVo := &vo.TenantAccessTokenRespVo{} 72 | json.FromJsonIgnoreError(respBody, respVo) 73 | return respVo, nil 74 | } 75 | 76 | //重新推送 app_ticket https://open.feishu.cn/document/ukTMukTMukTM/uQjNz4CN2MjL0YzM 77 | func AppTicketResend(appId, appSecret string) (*vo.CommonVo, error) { 78 | reqBody := map[string]interface{}{ 79 | "app_id": appId, 80 | "app_secret": appSecret, 81 | } 82 | respBody, err := http.Post(consts.ApiAppTicketResend, nil, json.ToJsonIgnoreError(reqBody)) 83 | if err != nil { 84 | log.Error(err) 85 | return nil, err 86 | } 87 | respVo := &vo.CommonVo{} 88 | json.FromJsonIgnoreError(respBody, respVo) 89 | return respVo, nil 90 | } 91 | 92 | //获取登录用户身份 https://open.feishu.cn/document/ukTMukTMukTM/ukTNz4SO1MjL5UzM 93 | func GetOauth2AccessToken(req vo.OAuth2AccessTokenReqVo) (*vo.OAuth2AccessTokenRespVo, error) { 94 | respBody, err := http.Post(consts.ApiOAuth2AccessToken, nil, json.ToJsonIgnoreError(req)) 95 | if err != nil { 96 | log.Error(err) 97 | return nil, err 98 | } 99 | respVo := &vo.OAuth2AccessTokenRespVo{} 100 | json.FromJsonIgnoreError(respBody, respVo) 101 | return respVo, nil 102 | } 103 | 104 | func AuthenAccessToken(appAccessToken string, grantType string, code string) (*vo.AuthenAccessTokenResp, error) { 105 | req := map[string]string{ 106 | "code": code, 107 | "grant_type": grantType, 108 | } 109 | respBody, err := http.Post(consts.ApiAuthenAccessToken, nil, json.ToJsonIgnoreError(req), http.BuildTokenHeaderOptions(appAccessToken)) 110 | if err != nil { 111 | log.Error(err) 112 | return nil, err 113 | } 114 | respVo := &vo.AuthenAccessTokenResp{} 115 | json.FromJsonIgnoreError(respBody, respVo) 116 | return respVo, nil 117 | } 118 | 119 | //获取用户信息 https://open.feishu.cn/document/ukTMukTMukTM/uAjNz4CM2MjLwYzM 120 | func GetOAuth2UserInfo(userAccessToken string) (*vo.OAuth2UserInfoRespVo, error) { 121 | respBody, err := http.Get(consts.ApiOAuth2GetUserInfoByAccessToken, nil, http.BuildTokenHeaderOptions(userAccessToken)) 122 | if err != nil { 123 | log.Error(err) 124 | return nil, err 125 | } 126 | respVo := &vo.OAuth2UserInfoRespVo{} 127 | json.FromJsonIgnoreError(respBody, respVo) 128 | return respVo, nil 129 | } 130 | 131 | //code2session https://open.feishu.cn/document/ukTMukTMukTM/ukjM04SOyQjL5IDN?lang=zh-CN 132 | func TokenLoginValidate(appAccessToken string, code string) (*vo.TokenLoginValidateResp, error) { 133 | req := map[string]string{ 134 | "code": code, 135 | } 136 | respBody, err := http.Post(consts.ApiTokenLoginValidate, nil, json.ToJsonIgnoreError(req), http.BuildTokenHeaderOptions(appAccessToken)) 137 | if err != nil { 138 | log.Error(err) 139 | return nil, err 140 | } 141 | respVo := &vo.TokenLoginValidateResp{} 142 | json.FromJsonIgnoreError(respBody, respVo) 143 | return respVo, nil 144 | } 145 | 146 | //刷新access_token https://open.feishu.cn/document/ukTMukTMukTM/uQDO4UjL0gDO14CN4gTN 147 | func RefreshUserAccessToken(appId, appSecret, appTicket, refreshToken string) (*vo.RefreshAccessTokenResp, error) { 148 | appAccessToken, err := GetAppAccessToken(appId, appSecret, appTicket) 149 | if err != nil { 150 | log.Error(err) 151 | return nil, err 152 | } 153 | 154 | req := map[string]string{ 155 | "app_access_token": appAccessToken.AppAccessToken, 156 | "grant_type": "refresh_token", 157 | "refresh_token": refreshToken, 158 | } 159 | respBody, err := http.Post(consts.ApiRefreshAccessToken, nil, json.ToJsonIgnoreError(req)) 160 | if err != nil { 161 | log.Error(err) 162 | return nil, err 163 | } 164 | respVo := &vo.RefreshAccessTokenResp{} 165 | json.FromJsonIgnoreError(respBody, respVo) 166 | return respVo, nil 167 | } 168 | -------------------------------------------------------------------------------- /sdk/auth_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestGetTenantAccessTokenInternal(t *testing.T) { 13 | resp, err := GetTenantAccessTokenInternal(consts.TestAppId, consts.TestAppSecret) 14 | log.Info(json.ToJsonIgnoreError(resp), err) 15 | assert.Equal(t, err, nil) 16 | assert.Equal(t, resp.Code, 0) 17 | } 18 | 19 | func TestGetAppAccessTokenInternal(t *testing.T) { 20 | resp, err := GetAppAccessToken(consts.TestAppId, consts.TestAppSecret, "") 21 | log.Info(json.ToJsonIgnoreError(resp), err) 22 | assert.Equal(t, err, nil) 23 | assert.Equal(t, resp.Code, 0) 24 | } 25 | 26 | func TestAppTicketResend(t *testing.T) { 27 | resp, err := AppTicketResend("cli_9d5e49aae9ae9101", "HDzPYfWmf8rmhsF2hHSvmhTffojOYCdI") 28 | log.Info(json.ToJsonIgnoreError(resp), err) 29 | assert.Equal(t, err, nil) 30 | assert.Equal(t, resp.Code, 0) 31 | // 32 | //resp, err = AppTicketResend("cli_9d40f5bf08f95108", "Apx5vdWeIxVzDBQ6ARte6grZgOCgbhgP") 33 | //log.Info(json.ToJsonIgnoreError(resp), err) 34 | //assert.Equal(t, err, nil) 35 | //assert.Equal(t, resp.Code, 0) 36 | // 37 | //resp, err = AppTicketResend("cli_9d511af667bd1101", "GESwFvoks5xQEr1t7dC4uhKR3cm1bg3S") 38 | //log.Info(json.ToJsonIgnoreError(resp), err) 39 | //assert.Equal(t, err, nil) 40 | //assert.Equal(t, resp.Code, 0) 41 | 42 | //resp, err = AppTicketResend("cli_9d3ae99f30eb9102", "O7Z43hPSFF1PHhRk8gVBOd3l36g8L5UG") 43 | //log.Info(json.ToJsonIgnoreError(resp), err) 44 | //assert.Equal(t, err, nil) 45 | //assert.Equal(t, resp.Code, 0) 46 | } 47 | 48 | func TestTokenLoginValidate(t *testing.T) { 49 | resp, err := TokenLoginValidate("a-566311d2cf88d054a4fcfc23233a448f2fccba11", "1c3a78be18ac815a") 50 | log.Info(json.ToJsonIgnoreError(resp), err) 51 | assert.Equal(t, err, nil) 52 | assert.Equal(t, resp.Code, 0) 53 | } 54 | 55 | func TestRefreshUserAccessToken(t *testing.T) { 56 | t.Log(RefreshUserAccessToken(consts.TestAppId, consts.TestAppSecret, consts.TestTicket, "ur-kprTLXLeDIRLA7ifLwu0Bd")) 57 | } 58 | 59 | func TestGetOauth2AccessToken(t *testing.T) { 60 | t.Log(GetOauth2AccessToken(vo.OAuth2AccessTokenReqVo{ 61 | AppId: consts.TestAppId, 62 | AppSecret: consts.TestAppSecret, 63 | //AppAccessToken: "a-4d0bea08bf46580b9cc9bf8edc0f5736fdfa7673", 64 | GrantType: "authorization_code", 65 | Code: "Ea3OYm95NV8Qn4IB2HxF5g", 66 | })) 67 | 68 | } 69 | 70 | func TestGetOAuth2UserInfo(t *testing.T) { 71 | t.Log(GetOAuth2UserInfo("u-e3PphRj4a4LiklDaeWjnre")) 72 | } 73 | 74 | func TestAuthenAccessToken(t *testing.T) { 75 | resp, err := AuthenAccessToken("a-8a2a27bcfeac6c4ebc1b48f2f8f60d4994261288", "authorization_code", "wF5dMARxrdwemww56sTefc") 76 | t.Log(json.ToJsonIgnoreError(resp)) 77 | assert.Equal(t, err, nil) 78 | assert.Equal(t, resp.Code, 0) 79 | } 80 | -------------------------------------------------------------------------------- /sdk/calendar.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 9 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 10 | ) 11 | 12 | //获取日历 https://open.feishu.cn/document/ukTMukTMukTM/uMDN04yM0QjLzQDN?lang=zh-CN 13 | func (t Tenant) GetCalendar(calendarId string) (*vo.CommonCalendarResp, error) { 14 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarGet, calendarId), nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 15 | if err != nil { 16 | log.Error(err) 17 | return nil, err 18 | } 19 | respVo := &vo.CommonCalendarResp{} 20 | json.FromJsonIgnoreError(respBody, respVo) 21 | return respVo, nil 22 | } 23 | 24 | //获取日历列表 25 | func (t Tenant) GetCalendarList(maxResults *int64, pageToken *string, syncToken *string) (*vo.CalendarListResp, error) { 26 | queryParams := map[string]interface{}{ 27 | "max_results": maxResults, 28 | "page_token": pageToken, 29 | "sync_token": syncToken, 30 | } 31 | respBody, err := http.Get(consts.ApiCalendarListGet, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 32 | if err != nil { 33 | log.Error(err) 34 | return nil, err 35 | } 36 | respVo := &vo.CalendarListResp{} 37 | json.FromJsonIgnoreError(respBody, respVo) 38 | return respVo, nil 39 | } 40 | 41 | //创建日历 https://open.feishu.cn/document/ukTMukTMukTM/uQTM14CNxUjL0ETN?lang=zh-CN 42 | func (t Tenant) CreateCalendar(bodyParams vo.CreateCalendarReq) (*vo.CommonCalendarResp, error) { 43 | respBody, err := http.Post(consts.ApiCalendarCreate, nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 44 | if err != nil { 45 | log.Error(err) 46 | return nil, err 47 | } 48 | respVo := &vo.CommonCalendarResp{} 49 | json.FromJsonIgnoreError(respBody, respVo) 50 | return respVo, nil 51 | } 52 | 53 | //更新日历 https://open.feishu.cn/document/ukTMukTMukTM/uYTM14iNxUjL2ETN?lang=zh-CN 54 | func (t Tenant) UpdateCalendar(calendarId string, bodyParams vo.UpdateCalendarReq) (*vo.CommonCalendarResp, error) { 55 | respBody, err := http.Patch(fmt.Sprintf(consts.ApiCalendarUpdate, calendarId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 56 | if err != nil { 57 | log.Error(err) 58 | return nil, err 59 | } 60 | respVo := &vo.CommonCalendarResp{} 61 | json.FromJsonIgnoreError(respBody, respVo) 62 | return respVo, nil 63 | } 64 | 65 | //删除日历 https://open.feishu.cn/document/ukTMukTMukTM/uUTM14SNxUjL1ETN?lang=zh-CN 66 | func (t Tenant) DeleteCalendar(calendarId string) (*vo.CommonVo, error) { 67 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarUpdate, calendarId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 68 | if err != nil { 69 | log.Error(err) 70 | return nil, err 71 | } 72 | respVo := &vo.CommonVo{} 73 | json.FromJsonIgnoreError(respBody, respVo) 74 | return respVo, nil 75 | } 76 | 77 | //获取日程列表 https://open.feishu.cn/document/ukTMukTMukTM/ukTM14SOxUjL5ETN 78 | func (t Tenant) GetCalendarEventList(calendarId string, maxResults *int64, pageToken *string, syncToken *string) (*vo.CalendarEventListResp, error) { 79 | queryParams := map[string]interface{}{ 80 | "max_results": maxResults, 81 | "page_token": pageToken, 82 | "sync_token": syncToken, 83 | } 84 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarEventCreate, calendarId), queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 85 | if err != nil { 86 | log.Error(err) 87 | return nil, err 88 | } 89 | respVo := &vo.CalendarEventListResp{} 90 | json.FromJsonIgnoreError(respBody, respVo) 91 | return respVo, nil 92 | } 93 | 94 | //创建日程 https://open.feishu.cn/document/ukTMukTMukTM/ugTM14COxUjL4ETN?lang=zh-CN 95 | func (t Tenant) CreateCalendarEvent(calendarId string, bodyParams vo.CreateCalendarEventReq) (*vo.CommonCalendarEventResp, error) { 96 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarEventCreate, calendarId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 97 | if err != nil { 98 | log.Error(err) 99 | return nil, err 100 | } 101 | respVo := &vo.CommonCalendarEventResp{} 102 | json.FromJsonIgnoreError(respBody, respVo) 103 | return respVo, nil 104 | } 105 | 106 | //删除日程 https://open.feishu.cn/document/ukTMukTMukTM/uAjM14CMyUjLwITN?lang=zh-CN 107 | func (t Tenant) DeleteCalendarEvent(calendarId string, eventId string) (*vo.CommonVo, error) { 108 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarEventDelete, calendarId, eventId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 109 | if err != nil { 110 | log.Error(err) 111 | return nil, err 112 | } 113 | respVo := &vo.CommonVo{} 114 | json.FromJsonIgnoreError(respBody, respVo) 115 | return respVo, nil 116 | } 117 | 118 | //更新日程 https://open.feishu.cn/document/ukTMukTMukTM/uEjM14SMyUjLxITN 119 | func (t Tenant) UpdateCalendarEvent(calendarId string, eventId string, bodyParams vo.CreateCalendarEventReq) (*vo.CommonCalendarEventResp, error) { 120 | respBody, err := http.Patch(fmt.Sprintf(consts.ApiCalendarEventDelete, calendarId, eventId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 121 | if err != nil { 122 | log.Error(err) 123 | return nil, err 124 | } 125 | respVo := &vo.CommonCalendarEventResp{} 126 | json.FromJsonIgnoreError(respBody, respVo) 127 | return respVo, nil 128 | } 129 | 130 | //邀请/移除日程参与者 https://open.feishu.cn/document/ukTMukTMukTM/uIjM14iMyUjLyITN 131 | func (t Tenant) UpdateCalendarEventAttendees(calendarId string, eventId string, bodyParams vo.UpdateCalendarEventAtendeesReq) (*vo.UpdateCalendarEventAtendeesResp, error) { 132 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarEventAttendeesUpdate, calendarId, eventId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 133 | if err != nil { 134 | log.Error(err) 135 | return nil, err 136 | } 137 | respVo := &vo.UpdateCalendarEventAtendeesResp{} 138 | json.FromJsonIgnoreError(respBody, respVo) 139 | return respVo, nil 140 | } 141 | 142 | //获取访问控制列表 https://open.feishu.cn/document/ukTMukTMukTM/uMjM14yMyUjLzITN 143 | func (t Tenant) GetCalendarAttendeesAcl(calendarId string) (*vo.GetCalendarAttendeesResp, error) { 144 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarAttendeesGet, calendarId), nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 145 | if err != nil { 146 | log.Error(err) 147 | return nil, err 148 | } 149 | respVo := &vo.GetCalendarAttendeesResp{} 150 | json.FromJsonIgnoreError(respBody, respVo) 151 | return respVo, nil 152 | } 153 | 154 | //创建访问控制 https://open.feishu.cn/document/ukTMukTMukTM/uQjM14CNyUjL0ITN 155 | func (t Tenant) AddCalendarAttendeesAcl(calendarId string, bodyParams vo.AddCalendarAttendeesAclReq) (*vo.GetCalendarAttendeesResp, error) { 156 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarAttendeesGet, calendarId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 157 | if err != nil { 158 | log.Error(err) 159 | return nil, err 160 | } 161 | respVo := &vo.GetCalendarAttendeesResp{} 162 | json.FromJsonIgnoreError(respBody, respVo) 163 | return respVo, nil 164 | } 165 | 166 | //删除访问控制 https://open.feishu.cn/document/ukTMukTMukTM/uUjM14SNyUjL1ITN 167 | func (t Tenant) DeleteCalendarAttendeesAcl(calendarId string, ruleId string) (*vo.CommonVo, error) { 168 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarAttendeesDelete, calendarId, ruleId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 169 | if err != nil { 170 | log.Error(err) 171 | return nil, err 172 | } 173 | respVo := &vo.CommonVo{} 174 | json.FromJsonIgnoreError(respBody, respVo) 175 | return respVo, nil 176 | } 177 | -------------------------------------------------------------------------------- /sdk/calendar_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | var calendarId = "feishu.cn_C13Hr0LgGiqNAJRak53Kea@group.calendar.feishu.cn" 13 | var calendarEventId = "9e1e3aed-3fa9-486c-99bf-4b42ffdbe384" 14 | 15 | func TestTenant_GetCalendar(t *testing.T) { 16 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 17 | t.Log(e) 18 | t.Log(json.ToJsonIgnoreError(app)) 19 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 20 | t.Log(e) 21 | 22 | resp, err := tenant.GetCalendar(calendarId) 23 | log.Info("日历:", json.ToJsonIgnoreError(resp), err) 24 | assert.Equal(t, err, nil) 25 | assert.Equal(t, resp.Code, 200000) 26 | 27 | resp2, err2 := tenant.GetCalendarList(nil, nil, nil) 28 | log.Info("日历列表:", json.ToJsonIgnoreError(resp2), err2) 29 | assert.Equal(t, err2, nil) 30 | assert.Equal(t, resp2.Code, 200000) 31 | } 32 | 33 | func TestTenant_CreateCalendar(t *testing.T) { 34 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 35 | t.Log(e) 36 | t.Log(json.ToJsonIgnoreError(app)) 37 | //tenant, e := BuildTenant(app.AppAccessToken, "130736cdb40f9758") 38 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 39 | t.Log(e) 40 | 41 | resp, err := tenant.CreateCalendar(vo.CreateCalendarReq{ 42 | Summary: "b", 43 | Description: "测试使用", 44 | }) 45 | log.Info(json.ToJsonIgnoreError(resp), err) 46 | assert.Equal(t, err, nil) 47 | assert.Equal(t, resp.Code, 200000) 48 | } 49 | 50 | func TestTenant_UpdateCalendar(t *testing.T) { 51 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 52 | t.Log(e) 53 | t.Log(json.ToJsonIgnoreError(app)) 54 | tenant, e := BuildTenant(app.AppAccessToken, "2c0c7abea54f9758") 55 | t.Log(e) 56 | 57 | resp, err := tenant.UpdateCalendar(calendarId, vo.UpdateCalendarReq{ 58 | Summary: "日历就是日历", 59 | Description: "测试使用哈", 60 | //DefaultAccessRole: "free_busy_reader", 61 | IsPrivate: true, 62 | }) 63 | log.Info(json.ToJsonIgnoreError(resp), err) 64 | assert.Equal(t, err, nil) 65 | assert.Equal(t, resp.Code, 200000) 66 | } 67 | 68 | func TestTenant_CreateCalendarEvent(t *testing.T) { 69 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 70 | t.Log(e) 71 | t.Log(json.ToJsonIgnoreError(app)) 72 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 73 | t.Log(e) 74 | 75 | attend := []vo.Attendees{} 76 | //attend = append(attend, vo.Attendees{ 77 | // OpenId: "ou_87f1b2210acad10a90cc3690802626d7", 78 | // EmployeeId: "", 79 | // DisplayName: "fanfan", 80 | // Optional: true, 81 | //}) 82 | resp, err := tenant.CreateCalendarEvent(calendarId, vo.CreateCalendarEventReq{ 83 | Summary: "测试测试不加用户怕", 84 | Description: "日程二号", 85 | Start: vo.TimeFormat{ 86 | Date: "2019-11-23", 87 | }, 88 | End: vo.TimeFormat{ 89 | Date: "2019-11-25", 90 | }, 91 | Attendees: &attend, 92 | }) 93 | log.Info(json.ToJsonIgnoreError(resp), err) 94 | assert.Equal(t, err, nil) 95 | assert.Equal(t, resp.Code, 200000) 96 | } 97 | 98 | func TestTenant_GetCalendarEventList(t *testing.T) { 99 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 100 | t.Log(e) 101 | t.Log(json.ToJsonIgnoreError(app)) 102 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 103 | t.Log(e) 104 | 105 | resp, err := tenant.GetCalendarEventList("feishu.cn_NEb1HqWfXyjSKu4zQ4fe3b@group.calendar.feishu.cn", nil, nil, nil) 106 | log.Info(json.ToJsonIgnoreError(resp), err) 107 | assert.Equal(t, err, nil) 108 | assert.Equal(t, resp.Code, 200000) 109 | } 110 | 111 | func TestTenant_UpdateCalendarEvent(t *testing.T) { 112 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 113 | t.Log(e) 114 | t.Log(json.ToJsonIgnoreError(app)) 115 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 116 | t.Log(e) 117 | 118 | resp, err := tenant.UpdateCalendarEvent(calendarId, calendarEventId, vo.CreateCalendarEventReq{ 119 | Summary: "好一个日程", 120 | Description: "hhh", 121 | Start: vo.TimeFormat{ 122 | Date: "2019-11-19", 123 | }, 124 | End: vo.TimeFormat{ 125 | Date: "2019-11-21", 126 | }, 127 | }) 128 | log.Info(json.ToJsonIgnoreError(resp), err) 129 | assert.Equal(t, err, nil) 130 | assert.Equal(t, resp.Code, 200000) 131 | } 132 | 133 | func TestTenant_DeleteCalendarEvent(t *testing.T) { 134 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 135 | t.Log(e) 136 | t.Log(json.ToJsonIgnoreError(app)) 137 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 138 | t.Log(e) 139 | 140 | resp, err := tenant.DeleteCalendarEvent("feishu.cn_NEb1HqWfXyjSKu4zQ4fe3b@group.calendar.feishu.cn", "3cca07ad-206e-41c5-92d9-29be0118b043") 141 | log.Info(json.ToJsonIgnoreError(resp), err) 142 | assert.Equal(t, err, nil) 143 | assert.Equal(t, resp.Code, 200000) 144 | } 145 | 146 | func TestTenant_UpdateCalendarEventAttendees(t *testing.T) { 147 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 148 | t.Log(e) 149 | t.Log(json.ToJsonIgnoreError(app)) 150 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 151 | t.Log(e) 152 | 153 | //{"employee_id":"","open_id":"ou_87f1b2210acad10a90cc3690802626d7","user_id":"","name":"樊宇","employee_no":""} 154 | attend := []vo.AttendeesResp{} 155 | attend = append(attend, vo.AttendeesResp{ 156 | Status: "invite", 157 | Attendees: vo.Attendees{ 158 | OpenId: "ou_87f1b2210acad10a90cc3690802626d7", 159 | EmployeeId: "", 160 | DisplayName: "fanfan", 161 | Optional: true, 162 | }, 163 | }) 164 | resp, err := tenant.UpdateCalendarEventAttendees("feishu.cn_NEb1HqWfXyjSKu4zQ4fe3b@group.calendar.feishu.cn", "3cca07ad-206e-41c5-92d9-29be0118b043", vo.UpdateCalendarEventAtendeesReq{ 165 | Attendees: attend, 166 | }) 167 | log.Info(json.ToJsonIgnoreError(resp), err) 168 | assert.Equal(t, err, nil) 169 | assert.Equal(t, resp.Code, 200000) 170 | } 171 | 172 | func TestTenant_GetCalendarAttendeesAcl(t *testing.T) { 173 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 174 | t.Log(e) 175 | t.Log(json.ToJsonIgnoreError(app)) 176 | tenant, e := BuildTenant(app.AppAccessToken, "2c0c7abea54f9758") 177 | t.Log(e) 178 | 179 | resp, err := tenant.GetCalendarAttendeesAcl(calendarId) 180 | log.Info(json.ToJsonIgnoreError(resp), err) 181 | assert.Equal(t, err, nil) 182 | assert.Equal(t, resp.Code, 200000) 183 | } 184 | 185 | func TestTenant_AddCalendarAttendeesAcl(t *testing.T) { 186 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 187 | t.Log(e) 188 | t.Log(json.ToJsonIgnoreError(app)) 189 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 190 | t.Log(e) 191 | 192 | resp, err := tenant.AddCalendarAttendeesAcl(calendarId, vo.AddCalendarAttendeesAclReq{ 193 | Role: consts.AccessRoleReader, 194 | Scope: vo.CalendarScope{ 195 | Type: "user", 196 | OpenId: "ou_87f1b2210acad10a90cc3690802626d7", 197 | }, 198 | }) 199 | log.Info(json.ToJsonIgnoreError(resp), err) 200 | assert.Equal(t, err, nil) 201 | assert.Equal(t, resp.Code, 200000) 202 | } 203 | 204 | func TestTenant_DeleteCalendarAttendeesAcl(t *testing.T) { 205 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 206 | t.Log(e) 207 | t.Log(json.ToJsonIgnoreError(app)) 208 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 209 | t.Log(e) 210 | resp, err := tenant.DeleteCalendarAttendeesAcl(calendarId, "ou_87f1b2210acad10a90cc3690802626d7") 211 | 212 | log.Info(json.ToJsonIgnoreError(resp), err) 213 | assert.Equal(t, err, nil) 214 | assert.Equal(t, resp.Code, 200000) 215 | } 216 | -------------------------------------------------------------------------------- /sdk/calendar_v4.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 9 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 10 | ) 11 | 12 | //CreateCalendarV4 创建日历v4版本 https://open.feishu.cn/document/ukTMukTMukTM/uUDM3YjL1AzN24SNwcjN 13 | func (t Tenant) CreateCalendarV4(bodyParams vo.CreateCalendarV4Req) (*vo.CommonCalendarV4Resp, error) { 14 | respBody, err := http.Post(consts.ApiCalendarCreateV4, nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 15 | if err != nil { 16 | log.Error(err) 17 | return nil, err 18 | } 19 | respVo := &vo.CommonCalendarV4Resp{} 20 | json.FromJsonIgnoreError(respBody, respVo) 21 | return respVo, nil 22 | } 23 | 24 | //创建日程v4 https://open.feishu.cn/document/ukTMukTMukTM/uYTM3YjL2EzN24iNxcjN 25 | func (t Tenant) CreateCalendarEventV4(calendarId string, bodyParams vo.CreateCalendarEventV4Req) (*vo.CalendarEventInfoV4Resp, error) { 26 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarEventCreateV4, calendarId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 27 | if err != nil { 28 | log.Error(err) 29 | return nil, err 30 | } 31 | respVo := &vo.CalendarEventInfoV4Resp{} 32 | json.FromJsonIgnoreError(respBody, respVo) 33 | return respVo, nil 34 | } 35 | 36 | //删除日程v4 https://open.feishu.cn/document/ukTMukTMukTM/ucTM3YjL3EzN24yNxcjN 37 | func (t Tenant) DeleteCalendarEventV4(calendarId string, eventId string) (*vo.CommonVo, error) { 38 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarEventDeleteV4, calendarId, eventId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 39 | if err != nil { 40 | log.Error(err) 41 | return nil, err 42 | } 43 | respVo := &vo.CommonVo{} 44 | json.FromJsonIgnoreError(respBody, respVo) 45 | return respVo, nil 46 | } 47 | 48 | //获取日程v4 https://open.feishu.cn/document/ukTMukTMukTM/ugTM3YjL4EzN24COxcjN 49 | func (t Tenant) GetCalendarEventV4(calendarId string, eventId string) (*vo.CalendarEventInfoV4Resp, error) { 50 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarEventGetV4, calendarId, eventId), nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 51 | if err != nil { 52 | log.Error(err) 53 | return nil, err 54 | } 55 | respVo := &vo.CalendarEventInfoV4Resp{} 56 | json.FromJsonIgnoreError(respBody, respVo) 57 | return respVo, nil 58 | } 59 | 60 | //获取日程列表v4 https://open.feishu.cn/document/ukTMukTMukTM/ukTM3YjL5EzN24SOxcjN 61 | func (t Tenant) GetCalendarEventListV4(calendarId string, eventId string, pageSize int, pageToken *string, syncToken *string) (*vo.GetCalendarEventListV4Resp, error) { 62 | queryParams := map[string]interface{}{ 63 | "page_size": pageSize, 64 | "page_token": pageToken, 65 | "sync_token": syncToken, 66 | } 67 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarEventBatchGetV4, calendarId), queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 68 | if err != nil { 69 | log.Error(err) 70 | return nil, err 71 | } 72 | respVo := &vo.GetCalendarEventListV4Resp{} 73 | json.FromJsonIgnoreError(respBody, respVo) 74 | return respVo, nil 75 | } 76 | 77 | //更新日程v4 https://open.feishu.cn/document/ukTMukTMukTM/uAjM3YjLwIzN24CMycjN 78 | func (t Tenant) UpdateCalendarEventV4(calendarId string, eventId string, bodyParams vo.UpdateCalendarEventV4Req) (*vo.CalendarEventInfoV4Resp, error) { 79 | respBody, err := http.Patch(fmt.Sprintf(consts.ApiCalendarEventUpdateV4, calendarId, eventId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 80 | if err != nil { 81 | log.Error(err) 82 | return nil, err 83 | } 84 | respVo := &vo.CalendarEventInfoV4Resp{} 85 | json.FromJsonIgnoreError(respBody, respVo) 86 | return respVo, nil 87 | } 88 | 89 | //创建日程参与人v4 https://open.feishu.cn/document/ukTMukTMukTM/uAjM3YjLwIzN24CMycjN 90 | func (t Tenant) AddCalendarEventAttendeesV4(calendarId string, eventId string, userIdType string, bodyParams vo.AddCalendarEventAttendeesV4Req) (*vo.AddCalendarEventAttendeesV4Resp, error) { 91 | params := map[string]interface{}{ 92 | "user_id_type": userIdType, 93 | } 94 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarEventAttendeesAddV4, calendarId, eventId), params, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 95 | if err != nil { 96 | log.Error(err) 97 | return nil, err 98 | } 99 | respVo := &vo.AddCalendarEventAttendeesV4Resp{} 100 | json.FromJsonIgnoreError(respBody, respVo) 101 | return respVo, nil 102 | } 103 | 104 | //删除日程参与人v4 https://open.feishu.cn/document/ukTMukTMukTM/uAzM3YjLwMzN24CMzcjN 105 | func (t Tenant) DeleteCalendarEventAttendeesV4(calendarId string, eventId string, userIdType string, bodyParams vo.DeleteCalendarEventAttendeesV4Req) (*vo.CommonVo, error) { 106 | params := map[string]interface{}{ 107 | "user_id_type": userIdType, 108 | } 109 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarEventAttendeesDeleteV4, calendarId, eventId), params, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 110 | if err != nil { 111 | log.Error(err) 112 | return nil, err 113 | } 114 | respVo := &vo.CommonVo{} 115 | json.FromJsonIgnoreError(respBody, respVo) 116 | return respVo, nil 117 | } 118 | 119 | //获取日程参与人列表v4 https://open.feishu.cn/document/ukTMukTMukTM/uEzM3YjLxMzN24SMzcjN 120 | func (t Tenant) GetCalendarEventAttendeesV4(calendarId string, eventId string, pageSize int, pageToken *string, userIdType string) (*vo.GetCalendarEventAttendeesV4Resp, error) { 121 | queryParams := map[string]interface{}{ 122 | "page_size": pageSize, 123 | "page_token": pageToken, 124 | "user_id_type": userIdType, 125 | } 126 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarEventAttendeesGetV4, calendarId, eventId), queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 127 | if err != nil { 128 | log.Error(err) 129 | return nil, err 130 | } 131 | respVo := &vo.GetCalendarEventAttendeesV4Resp{} 132 | json.FromJsonIgnoreError(respBody, respVo) 133 | return respVo, nil 134 | } 135 | 136 | //获取参与人群成员列表v4 https://open.feishu.cn/document/ukTMukTMukTM/uATN3YjLwUzN24CM1cjN 137 | func (t Tenant) GetCalendarEventAttendeesChatMembersV4(calendarId string, eventId string, attendeeId string, pageSize int, pageToken *string, userIdType string) (*vo.GetCalendarEventAttendeesV4Resp, error) { 138 | queryParams := map[string]interface{}{ 139 | "page_size": pageSize, 140 | "page_token": pageToken, 141 | "user_id_type": userIdType, 142 | } 143 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarEventAttendeesChatMembersGetV4, calendarId, eventId, attendeeId), queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 144 | if err != nil { 145 | log.Error(err) 146 | return nil, err 147 | } 148 | respVo := &vo.GetCalendarEventAttendeesV4Resp{} 149 | json.FromJsonIgnoreError(respBody, respVo) 150 | return respVo, nil 151 | } 152 | 153 | //DeleteCalendarV4 删除日历 https://open.feishu.cn/document/ukTMukTMukTM/uYDM3YjL2AzN24iNwcjN 154 | func (t Tenant) DeleteCalendarV4(calendarId string) (*vo.VoidV4Resp, error) { 155 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarDeleteV4, calendarId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 156 | if err != nil { 157 | log.Error(err) 158 | return nil, err 159 | } 160 | respVo := &vo.VoidV4Resp{} 161 | json.FromJsonIgnoreError(respBody, respVo) 162 | return respVo, nil 163 | } 164 | 165 | //GetCalendarV4 获取日历 https://open.feishu.cn/document/ukTMukTMukTM/ucDM3YjL3AzN24yNwcjN 166 | func (t Tenant) GetCalendarV4(calendarId string) (*vo.GetCalendarV4Resp, error) { 167 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarGetV4, calendarId), nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 168 | if err != nil { 169 | log.Error(err) 170 | return nil, err 171 | } 172 | respVo := &vo.GetCalendarV4Resp{} 173 | json.FromJsonIgnoreError(respBody, respVo) 174 | return respVo, nil 175 | } 176 | 177 | // 获取日历列表 https://open.feishu.cn/document/ukTMukTMukTM/ugDM3YjL4AzN24COwcjN todo 178 | func (t Tenant) GetCalendarListV4(pageSize int, pageToken, syncToken string) (*vo.GetCalendarListV4Resp, error) { 179 | queryParams := map[string]interface{}{ 180 | "page_size": pageSize, 181 | "page_token": pageToken, 182 | "sync_token": syncToken, 183 | } 184 | respBody, err := http.Post(consts.ApiCalendarGetListV4, queryParams, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 185 | if err != nil { 186 | log.Error(err) 187 | return nil, err 188 | } 189 | respVo := &vo.GetCalendarListV4Resp{} 190 | json.FromJsonIgnoreError(respBody, respVo) 191 | return respVo, nil 192 | } 193 | 194 | //UpdateCalendarV4 更新日历 https://open.feishu.cn/document/ukTMukTMukTM/ukDM3YjL5AzN24SOwcjN 195 | func (t Tenant) UpdateCalendarV4(calendarId string, bodyParams vo.UpdateCalendarV4Req) (*vo.CommonCalendarV4Resp, error) { 196 | respBody, err := http.Patch(fmt.Sprintf(consts.ApiCalendarUpdateV4, calendarId), nil, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 197 | if err != nil { 198 | log.Error(err) 199 | return nil, err 200 | } 201 | respVo := &vo.CommonCalendarV4Resp{} 202 | json.FromJsonIgnoreError(respBody, respVo) 203 | return respVo, nil 204 | } 205 | 206 | //SearchCalendarV4 搜索日历 https://open.feishu.cn/document/ukTMukTMukTM/uATM3YjLwEzN24CMxcjN 207 | func (t Tenant) SearchCalendarV4(bodyParams vo.SearchCalendarV4Req, pageToken string, pageSize int) (*vo.SearchCalendarV4Resp, error) { 208 | queryParams := map[string]interface{}{ 209 | "page_token": pageToken, 210 | "page_size": pageSize, 211 | } 212 | respBody, err := http.Post(consts.ApiCalendarSearchV4, queryParams, json.ToJsonIgnoreError(bodyParams), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 213 | if err != nil { 214 | log.Error(err) 215 | return nil, err 216 | } 217 | respVo := &vo.SearchCalendarV4Resp{} 218 | json.FromJsonIgnoreError(respBody, respVo) 219 | return respVo, nil 220 | } 221 | 222 | //UnsubscribeCalendarV4 取消订阅日历 https://open.feishu.cn/document/ukTMukTMukTM/ugDO3YjL4gzN24CO4cjN 223 | func (t Tenant) UnsubscribeCalendarV4(calendarId string) (*vo.VoidV4Resp, error) { 224 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarUnsubscribeV4, calendarId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 225 | if err != nil { 226 | log.Error(err) 227 | return nil, err 228 | } 229 | respVo := &vo.VoidV4Resp{} 230 | json.FromJsonIgnoreError(respBody, respVo) 231 | return respVo, nil 232 | } 233 | 234 | //SubscribeCalendarV4 订阅日历 https://open.feishu.cn/document/ukTMukTMukTM/ucDO3YjL3gzN24yN4cjN 235 | func (t Tenant) SubscribeCalendarV4(calendarId string) (*vo.SubscribeCalendarV4Resp, error) { 236 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarSubscribeV4, calendarId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 237 | if err != nil { 238 | log.Error(err) 239 | return nil, err 240 | } 241 | respVo := &vo.SubscribeCalendarV4Resp{} 242 | json.FromJsonIgnoreError(respBody, respVo) 243 | return respVo, nil 244 | } 245 | 246 | //SubscriptionCalendarV4 订阅日历变更事件 https://open.feishu.cn/document/ukTMukTMukTM/ugTO2YjL4kjN24CO5YjN/subscribe%20calendar%20changed%20event 247 | func (t Tenant) SubscriptionCalendarV4() (*vo.VoidV4Resp, error) { 248 | respBody, err := http.Post(consts.ApiCalendarSubscriptionV4, nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 249 | if err != nil { 250 | log.Error(err) 251 | return nil, err 252 | } 253 | respVo := &vo.VoidV4Resp{} 254 | json.FromJsonIgnoreError(respBody, respVo) 255 | return respVo, nil 256 | } 257 | 258 | //创建访问控制 https://open.feishu.cn/document/ukTMukTMukTM/uUjM3YjL1IzN24SNycjN 259 | func (t Tenant) AddCalendarAclV4(calendarId string, userIdType string, body vo.AddCalendarAclV4Req) (*vo.AddCalendarAclV4Resp, error) { 260 | params := map[string]interface{}{ 261 | "user_id_type": userIdType, 262 | } 263 | respBody, err := http.Post(fmt.Sprintf(consts.ApiCalendarAddCalendarAclV4, calendarId), params, json.ToJsonIgnoreError(body), http.BuildTokenHeaderOptions(t.TenantAccessToken)) 264 | if err != nil { 265 | log.Error(err) 266 | return nil, err 267 | } 268 | respVo := &vo.AddCalendarAclV4Resp{} 269 | json.FromJsonIgnoreError(respBody, respVo) 270 | return respVo, nil 271 | } 272 | 273 | //删除访问控制 https://open.feishu.cn/document/ukTMukTMukTM/uYjM3YjL2IzN24iNycjN 274 | func (t Tenant) DeleteCalendarAclV4(calendarId string, aclId string) (*vo.CommonVo, error) { 275 | respBody, err := http.Delete(fmt.Sprintf(consts.ApiCalendarDeleteCalendarAclV4, calendarId, aclId), nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 276 | if err != nil { 277 | log.Error(err) 278 | return nil, err 279 | } 280 | respVo := &vo.CommonVo{} 281 | json.FromJsonIgnoreError(respBody, respVo) 282 | return respVo, nil 283 | } 284 | 285 | //获取访问控制列表 https://open.feishu.cn/document/ukTMukTMukTM/ucjM3YjL3IzN24yNycjN 286 | func (t Tenant) GetCalendarAclList(calendarId string, pageSize int, pageToken string) (*vo.GetCalendarAclListV4Resp, error) { 287 | params := map[string]interface{}{} 288 | if pageSize > 0 { 289 | params["page_size"] = pageSize 290 | } 291 | if pageToken != "" { 292 | params["page_token"] = pageToken 293 | } 294 | respBody, err := http.Get(fmt.Sprintf(consts.ApiCalendarCalendarAclGetV4, calendarId), params, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 295 | if err != nil { 296 | log.Error(err) 297 | return nil, err 298 | } 299 | respVo := &vo.GetCalendarAclListV4Resp{} 300 | json.FromJsonIgnoreError(respBody, respVo) 301 | return respVo, nil 302 | } 303 | -------------------------------------------------------------------------------- /sdk/calendar_v4_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | var calendarIdV4 = "feishu.cn_hLMJfum0W0njdhJrycO3bb@group.calendar.feishu.cn" 13 | var eventIdV4 = "a7f6ab60-a239-45e0-87a7-a19c550e37ef_0" 14 | 15 | func TestTenant_CreateCalendarV4(t *testing.T) { 16 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 17 | t.Log(e) 18 | t.Log(json.ToJsonIgnoreError(app)) 19 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 20 | t.Log(e) 21 | 22 | resp, err := tenant.CreateCalendarV4(vo.CreateCalendarV4Req{ 23 | Summary: "BBB", 24 | Description: "AAAtest", 25 | Permissions: "public", 26 | }) 27 | log.Info(json.ToJsonIgnoreError(resp), err) 28 | assert.Equal(t, err, nil) 29 | assert.Equal(t, resp.Code, 200000) 30 | } 31 | 32 | func TestTenant_DeleteCalendarV4(t *testing.T) { 33 | tenantKey := "130736cdb40f9758" 34 | tenant := GetTenant(tenantKey) 35 | resp, err := tenant.DeleteCalendarV4("") 36 | if err != nil { 37 | t.Error(err) 38 | return 39 | } 40 | t.Log(json.ToJsonIgnoreError(resp)) 41 | } 42 | 43 | func TestTenant_GetCalendarV4(t *testing.T) { 44 | tenantKey := "2e99b3ab0b0f1654" 45 | tenant := GetTenant(tenantKey) 46 | resp, err := tenant.GetCalendarV4(calendarIdV4) 47 | if err != nil { 48 | t.Error(err) 49 | return 50 | } 51 | t.Log(json.ToJsonIgnoreError(resp)) 52 | } 53 | 54 | func TestTenant_GetCalendarListV4(t *testing.T) { 55 | tenantKey := "2e99b3ab0b0f1654" 56 | tenant := GetTenant(tenantKey) 57 | resp, err := tenant.GetCalendarListV4(10, "", "") 58 | if err != nil { 59 | t.Error(err) 60 | return 61 | } 62 | t.Log(json.ToJsonIgnoreError(resp)) 63 | } 64 | 65 | func TestTenant_UpdateCalendarV4(t *testing.T) { 66 | tenantKey := "2e99b3ab0b0f1654" 67 | calendarId := "feishu.cn_Y8iPaCjvuBeG6WEdJ326qg@group.calendar.feishu.cn" 68 | tenant := GetTenant(tenantKey) 69 | resp, err := tenant.UpdateCalendarV4(calendarId, vo.UpdateCalendarV4Req{ 70 | Summary: "测试001", 71 | Description: "测试001 desc", 72 | Permissions: "public", 73 | Color: 0, 74 | SummaryAlias: "", 75 | CalendarId: calendarId, 76 | }) 77 | if err != nil { 78 | t.Error(err) 79 | return 80 | } 81 | t.Log(json.ToJsonIgnoreError(resp)) 82 | } 83 | 84 | func TestTenant_SearchCalendarV4(t *testing.T) { 85 | tenantKey := "2e99b3ab0b0f1654" 86 | tenant := GetTenant(tenantKey) 87 | resp, err := tenant.SearchCalendarV4(vo.SearchCalendarV4Req{ 88 | Query: "测试", 89 | }, "", 10) 90 | if err != nil { 91 | t.Error(err) 92 | return 93 | } 94 | t.Log(json.ToJsonIgnoreError(resp)) 95 | } 96 | 97 | func TestTenant_SubscribeCalendarV4(t *testing.T) { 98 | tenantKey := "2e99b3ab0b0f1654" 99 | calendarId := "feishu.cn_Y8iPaCjvuBeG6WEdJ326qg@group.calendar.feishu.cn" 100 | tenant := GetTenant(tenantKey) 101 | resp, err := tenant.SubscribeCalendarV4(calendarId) 102 | if err != nil { 103 | t.Error(err) 104 | return 105 | } 106 | t.Log(json.ToJsonIgnoreError(resp)) 107 | } 108 | 109 | func TestTenant_UnsubscribeCalendarV4(t *testing.T) { 110 | tenantKey := "2e99b3ab0b0f1654" 111 | calendarId := "feishu.cn_Y8iPaCjvuBeG6WEdJ326qg@group.calendar.feishu.cn" 112 | tenant := GetTenant(tenantKey) 113 | resp, err := tenant.UnsubscribeCalendarV4(calendarId) 114 | if err != nil { 115 | t.Error(err) 116 | return 117 | } 118 | t.Log(json.ToJsonIgnoreError(resp)) 119 | } 120 | 121 | func TestTenant_CreateCalendarEventV4(t *testing.T) { 122 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 123 | t.Log(e) 124 | t.Log(json.ToJsonIgnoreError(app)) 125 | tenant, e := BuildTenant(app.AppAccessToken, "13e56c12788e575f") 126 | t.Log(e) 127 | 128 | resp, err := tenant.CreateCalendarEventV4(calendarIdV4, vo.CreateCalendarEventV4Req{ 129 | Summary: "a", 130 | Description: "", 131 | StartTime: vo.CalendarEventTime{ 132 | Timestamp: "1617176660", 133 | }, 134 | EndTime: vo.CalendarEventTime{ 135 | Timestamp: "1617177660", 136 | }, 137 | //Vchat: vo.Vchat{}, 138 | //Visibility: "public", 139 | //AttendeeAbility: "", 140 | //FreeBusyStatus: "", 141 | //Location: vo.Location{}, 142 | Color: 0, 143 | Reminders: nil, 144 | Recurrence: "", 145 | Schemas: nil, 146 | }) 147 | log.Info(json.ToJsonIgnoreError(resp), err) 148 | assert.Equal(t, err, nil) 149 | assert.Equal(t, resp.Code, 0) 150 | } 151 | 152 | func TestTenant_DeleteCalendarEventV4(t *testing.T) { 153 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 154 | t.Log(e) 155 | t.Log(json.ToJsonIgnoreError(app)) 156 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 157 | t.Log(e) 158 | 159 | resp, err := tenant.DeleteCalendarEventV4(calendarIdV4, eventIdV4) 160 | 161 | log.Info(json.ToJsonIgnoreError(resp), err) 162 | assert.Equal(t, err, nil) 163 | assert.Equal(t, resp.Code, 0) 164 | } 165 | 166 | func TestTenant_GetCalendarEventV4(t *testing.T) { 167 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 168 | t.Log(e) 169 | t.Log(json.ToJsonIgnoreError(app)) 170 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 171 | t.Log(e) 172 | 173 | resp, err := tenant.GetCalendarEventV4(calendarIdV4, eventIdV4) 174 | 175 | log.Info(json.ToJsonIgnoreError(resp), err) 176 | assert.Equal(t, err, nil) 177 | assert.Equal(t, resp.Code, 0) 178 | 179 | resp1, err1 := tenant.GetCalendarEventListV4(calendarIdV4, eventIdV4, 50, nil, nil) 180 | 181 | log.Info(json.ToJsonIgnoreError(resp1), err1) 182 | assert.Equal(t, err1, nil) 183 | assert.Equal(t, resp1.Code, 0) 184 | } 185 | 186 | func TestTenant_UpdateCalendarEventV4(t *testing.T) { 187 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 188 | t.Log(e) 189 | t.Log(json.ToJsonIgnoreError(app)) 190 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 191 | t.Log(e) 192 | 193 | summary := "aaa" 194 | description := "des" 195 | resp, err := tenant.UpdateCalendarEventV4(calendarIdV4, eventIdV4, vo.UpdateCalendarEventV4Req{ 196 | Summary: &summary, 197 | Description: &description, 198 | }) 199 | 200 | log.Info(json.ToJsonIgnoreError(resp), err) 201 | assert.Equal(t, err, nil) 202 | assert.Equal(t, resp.Code, 0) 203 | } 204 | 205 | func TestTenant_AddCalendarEventAttendeesV4(t *testing.T) { 206 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 207 | t.Log(e) 208 | t.Log(json.ToJsonIgnoreError(app)) 209 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 210 | t.Log(e) 211 | 212 | resp, err := tenant.AddCalendarEventAttendeesV4(calendarIdV4, eventIdV4, "open_id", vo.AddCalendarEventAttendeesV4Req{ 213 | Attendees: []vo.AttendeesV4{ 214 | vo.AttendeesV4{ 215 | Type: "user", 216 | IsOptional: false, 217 | UserId: "ou_3018c5fbbb152f1b1b8f4c1c547df2b9", 218 | }, 219 | }, 220 | }) 221 | 222 | log.Info(json.ToJsonIgnoreError(resp), err) 223 | assert.Equal(t, err, nil) 224 | assert.Equal(t, resp.Code, 0) 225 | } 226 | 227 | func TestTenant_GetCalendarEventAttendeesV4(t *testing.T) { 228 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 229 | t.Log(e) 230 | t.Log(json.ToJsonIgnoreError(app)) 231 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 232 | t.Log(e) 233 | 234 | resp, err := tenant.GetCalendarEventAttendeesV4(calendarIdV4, eventIdV4, 0, nil, "open_id") 235 | 236 | log.Info(json.ToJsonIgnoreError(resp), err) 237 | assert.Equal(t, err, nil) 238 | assert.Equal(t, resp.Code, 0) 239 | } 240 | 241 | func TestTenant_DeleteCalendarEventAttendeesV4(t *testing.T) { 242 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 243 | t.Log(e) 244 | t.Log(json.ToJsonIgnoreError(app)) 245 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 246 | t.Log(e) 247 | 248 | resp, err := tenant.DeleteCalendarEventAttendeesV4(calendarIdV4, eventIdV4, "open_id", vo.DeleteCalendarEventAttendeesV4Req{ 249 | AttendeeIds: []string{"user_6772353233718345997"}, 250 | }) 251 | 252 | log.Info(json.ToJsonIgnoreError(resp), err) 253 | assert.Equal(t, err, nil) 254 | assert.Equal(t, resp.Code, 0) 255 | } 256 | 257 | func TestTenant_AddCalendarAclV4(t *testing.T) { 258 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 259 | t.Log(e) 260 | t.Log(json.ToJsonIgnoreError(app)) 261 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 262 | t.Log(e) 263 | 264 | resp, err := tenant.AddCalendarAclV4(calendarIdV4, "open_id", vo.AddCalendarAclV4Req{ 265 | Role: "writer", 266 | Scope: vo.AclScope{ 267 | Type: "user", 268 | UserId: "ou_ce397f53085cb712373f71874e8eae78", 269 | }, 270 | }) 271 | 272 | log.Info(json.ToJsonIgnoreError(resp), err) 273 | assert.Equal(t, err, nil) 274 | assert.Equal(t, resp.Code, 0) 275 | } 276 | 277 | func TestTenant_GetCalendarAclList(t *testing.T) { 278 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 279 | t.Log(e) 280 | t.Log(json.ToJsonIgnoreError(app)) 281 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 282 | t.Log(e) 283 | 284 | resp, err := tenant.GetCalendarAclList(calendarIdV4, 0, "") 285 | 286 | log.Info(json.ToJsonIgnoreError(resp), err) 287 | assert.Equal(t, err, nil) 288 | assert.Equal(t, resp.Code, 0) 289 | } 290 | 291 | func TestTenant_DeleteCalendarAclV4(t *testing.T) { 292 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 293 | t.Log(e) 294 | t.Log(json.ToJsonIgnoreError(app)) 295 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 296 | t.Log(e) 297 | 298 | resp, err := tenant.DeleteCalendarAclV4(calendarIdV4, "user_6773628684575179019") 299 | 300 | log.Info(json.ToJsonIgnoreError(resp), err) 301 | assert.Equal(t, err, nil) 302 | assert.Equal(t, resp.Code, 0) 303 | } 304 | -------------------------------------------------------------------------------- /sdk/chat.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 9 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 10 | "net/url" 11 | ) 12 | 13 | //获取用户所在的群列表 https://open.feishu.cn/document/ukTMukTMukTM/uQzMwUjL0MDM14CNzATN 14 | func (t Tenant) GroupList(userAccessToken string, pageSize int, pageToken string) (*vo.GroupListRespVo, error) { 15 | queryParams := map[string]interface{}{} 16 | if pageSize > 0 { 17 | queryParams["page_size"] = pageSize 18 | } 19 | if pageToken != "" { 20 | queryParams["page_token"] = pageToken 21 | } 22 | respBody, err := http.Get(consts.ApiUserGroupLIst, queryParams, http.BuildTokenHeaderOptions(userAccessToken)) 23 | if err != nil { 24 | log.Error(err) 25 | return nil, err 26 | } 27 | respVo := &vo.GroupListRespVo{} 28 | json.FromJsonIgnoreError(respBody, respVo) 29 | return respVo, nil 30 | } 31 | 32 | //获取群成员列表 https://open.feishu.cn/document/ukTMukTMukTM/uUzMwUjL1MDM14SNzATN 33 | func (t Tenant) ChatMembers(userAccessToken string, chatId string, pageSize int, pageToken string) (*vo.ChatMembersRespVo, error) { 34 | queryParams := map[string]interface{}{ 35 | "chat_id": chatId, 36 | } 37 | if pageSize > 0 { 38 | queryParams["page_size"] = pageSize 39 | } 40 | if pageToken != "" { 41 | queryParams["page_token"] = pageToken 42 | } 43 | respBody, err := http.Get(consts.ApiChatMembers, queryParams, http.BuildTokenHeaderOptions(userAccessToken)) 44 | if err != nil { 45 | log.Error(err) 46 | return nil, err 47 | } 48 | respVo := &vo.ChatMembersRespVo{} 49 | json.FromJsonIgnoreError(respBody, respVo) 50 | return respVo, nil 51 | } 52 | 53 | //搜索用户所在的群列表 https://open.feishu.cn/document/ukTMukTMukTM/uUTOyUjL1kjM14SN5ITN 54 | func (t Tenant) ChatSearch(userAccessToken string, query string, pageSize int, pageToken string) (*vo.GroupListRespVo, error) { 55 | queryParams := map[string]interface{}{ 56 | "query": url.QueryEscape(query), 57 | } 58 | if pageSize > 0 { 59 | queryParams["page_size"] = pageSize 60 | } 61 | if pageToken != "" { 62 | queryParams["page_token"] = pageToken 63 | } 64 | respBody, err := http.Get(consts.ApiChatSearch, queryParams, http.BuildTokenHeaderOptions(userAccessToken)) 65 | if err != nil { 66 | log.Error(err) 67 | return nil, err 68 | } 69 | respVo := &vo.GroupListRespVo{} 70 | json.FromJsonIgnoreError(respBody, respVo) 71 | return respVo, nil 72 | } 73 | 74 | //获取用户或机器人所在的群列表 https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat/list 75 | func (t Tenant) ImChatList(userAccessToken string, pageSize int, pageToken string, userIdType *string) (*vo.ImChatListRespVo, error) { 76 | queryParams := map[string]interface{}{} 77 | if pageSize > 0 { 78 | queryParams["page_size"] = pageSize 79 | } 80 | if pageToken != "" { 81 | queryParams["page_token"] = pageToken 82 | } 83 | if userIdType != nil { 84 | queryParams["user_id_type"] = *userIdType 85 | } 86 | 87 | accessToken := t.TenantAccessToken 88 | if userAccessToken != "" { 89 | accessToken = userAccessToken 90 | } 91 | respBody, err := http.Get(consts.ApiImChatList, queryParams, http.BuildTokenHeaderOptions(accessToken)) 92 | if err != nil { 93 | log.Error(err) 94 | return nil, err 95 | } 96 | 97 | respVo := &vo.ImChatListRespVo{} 98 | json.FromJsonIgnoreError(respBody, respVo) 99 | return respVo, nil 100 | } 101 | 102 | //获取群信息 https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat/get 103 | func (t Tenant) ImChatInfo(userAccessToken string, chatId string) (*vo.ImChatInfoRespVo, error) { 104 | accessToken := t.TenantAccessToken 105 | if userAccessToken != "" { 106 | accessToken = userAccessToken 107 | } 108 | respBody, err := http.Get(fmt.Sprintf("%s%s", consts.ApiImChatInfo, chatId), nil, http.BuildTokenHeaderOptions(accessToken)) 109 | if err != nil { 110 | log.Error(err) 111 | return nil, err 112 | } 113 | 114 | respVo := &vo.ImChatInfoRespVo{} 115 | json.FromJsonIgnoreError(respBody, respVo) 116 | return respVo, nil 117 | } 118 | -------------------------------------------------------------------------------- /sdk/chat_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 7 | "gotest.tools/assert" 8 | "testing" 9 | ) 10 | 11 | func TestTenant_GroupList(t *testing.T) { 12 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 13 | t.Log(e) 14 | t.Log(json.ToJsonIgnoreError(app)) 15 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 16 | t.Log(e) 17 | 18 | resp, err := tenant.GroupList("u-OVRWRbISgBZK6j9pu2ApJg", 0, "") 19 | log.Info(json.ToJsonIgnoreError(resp), err) 20 | assert.Equal(t, err, nil) 21 | assert.Equal(t, resp.Code, 0) 22 | } 23 | 24 | func TestTenant_ChatMembers(t *testing.T) { 25 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 26 | t.Log(e) 27 | t.Log(json.ToJsonIgnoreError(app)) 28 | tenant, e := BuildTenant(app.AppAccessToken, "1319b042200f575e") 29 | t.Log(e) 30 | 31 | resp, err := tenant.ChatMembers("u-mY81BLL4kurNqgbN0tsFka", "oc_a414a24110b73441307413f1bf8f2a33", 0, "") 32 | log.Info(json.ToJsonIgnoreError(resp), err) 33 | assert.Equal(t, err, nil) 34 | assert.Equal(t, resp.Code, 0) 35 | } 36 | 37 | func TestTenant_ChatSearch(t *testing.T) { 38 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 39 | t.Log(e) 40 | t.Log(json.ToJsonIgnoreError(app)) 41 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 42 | t.Log(e) 43 | 44 | resp, err := tenant.ChatSearch("u-OVRWRbISgBZK6j9pu2ApJg", "北极星测试企业。", 0, "") 45 | log.Info(json.ToJsonIgnoreError(resp), err) 46 | assert.Equal(t, err, nil) 47 | assert.Equal(t, resp.Code, 0) 48 | } 49 | 50 | func TestTenant_ImChatList(t *testing.T) { 51 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 52 | t.Log(e) 53 | t.Log(json.ToJsonIgnoreError(app)) 54 | tenant, e := BuildTenant(app.AppAccessToken, "1279794b670f575f") 55 | t.Log(e) 56 | 57 | resp, err := tenant.ImChatList("", 0, "", nil) 58 | log.Info(json.ToJsonIgnoreError(resp), err) 59 | assert.Equal(t, err, nil) 60 | assert.Equal(t, resp.Code, 0) 61 | } 62 | -------------------------------------------------------------------------------- /sdk/department.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //获取通讯录授权范围 https://open.feishu.cn/document/ukTMukTMukTM/ugjNz4CO2MjL4YzM?lang=zh-CN 12 | func (t Tenant) GetScope() (*vo.GetScopeRespVo, error) { 13 | respBody, err := http.Get(consts.ApiScope, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 14 | if err != nil { 15 | log.Error(err) 16 | return nil, err 17 | } 18 | respVo := &vo.GetScopeRespVo{} 19 | json.FromJsonIgnoreError(respBody, respVo) 20 | return respVo, nil 21 | } 22 | 23 | //获取通讯录授权范围 v2 https://open.feishu.cn/document/ukTMukTMukTM/ugjNz4CO2MjL4YzM?lang=zh-CN 24 | func (t Tenant) GetScopeV2() (*vo.GetScopeRespV2Vo, error) { 25 | respBody, err := http.Get(consts.ApiScopeV2, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 26 | if err != nil { 27 | log.Error(err) 28 | return nil, err 29 | } 30 | respVo := &vo.GetScopeRespV2Vo{} 31 | json.FromJsonIgnoreError(respBody, respVo) 32 | return respVo, nil 33 | } 34 | 35 | //获取子部门列表 https://open.feishu.cn/document/ukTMukTMukTM/ugzN3QjL4czN04CO3cDN 36 | func (t Tenant) GetDepartmentSimpleList(departmentId string, offset, pageSize int, fetchChild bool) (*vo.GetDepartmentSimpleListRespVo, error) { 37 | queryParams := map[string]interface{}{ 38 | "department_id": departmentId, 39 | "offset": offset, 40 | "page_size": pageSize, 41 | "fetch_child": fetchChild, 42 | } 43 | respBody, err := http.Get(consts.ApiDepartmentSimpleList, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 44 | if err != nil { 45 | log.Error(err) 46 | return nil, err 47 | } 48 | respVo := &vo.GetDepartmentSimpleListRespVo{} 49 | json.FromJsonIgnoreError(respBody, respVo) 50 | return respVo, nil 51 | } 52 | 53 | //获取子部门列表v2 https://open.feishu.cn/document/ukTMukTMukTM/ugzN3QjL4czN04CO3cDN 54 | func (t Tenant) GetDepartmentSimpleListV2(id string, pageToken string, pageSize int, fetchChild bool) (*vo.GetDepartmentSimpleListV2RespVo, error) { 55 | queryParams := map[string]interface{}{ 56 | "id": id, 57 | "fetch_child": fetchChild, 58 | } 59 | if pageToken != "" { 60 | queryParams["page_token"] = pageToken 61 | } 62 | if pageSize > 0 { 63 | queryParams["page_size"] = pageSize 64 | } 65 | 66 | respBody, err := http.Get(consts.ApiDepartmentSimpleListV2, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 67 | if err != nil { 68 | log.Error(err) 69 | return nil, err 70 | } 71 | respVo := &vo.GetDepartmentSimpleListV2RespVo{} 72 | json.FromJsonIgnoreError(respBody, respVo) 73 | return respVo, nil 74 | } 75 | 76 | //获取部门详情 https://open.feishu.cn/document/ukTMukTMukTM/uAzNz4CM3MjLwczM 77 | func (t Tenant) GetDepartmentInfo(departmentId string) (*vo.GetDepartmentInfoRespVo, error) { 78 | queryParams := map[string]interface{}{ 79 | "department_id": departmentId, 80 | } 81 | respBody, err := http.Get(consts.ApiDepartmentInfoGet, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 82 | if err != nil { 83 | log.Error(err) 84 | return nil, err 85 | } 86 | respVo := &vo.GetDepartmentInfoRespVo{} 87 | json.FromJsonIgnoreError(respBody, respVo) 88 | return respVo, nil 89 | } 90 | 91 | //批量获取部门详情 https://bytedance.feishu.cn/docs/doccnOcR1fnxBACchoY9tlg7Amg# 92 | func (t Tenant) GetDepartmentInfoBatch(depIds []string) (*vo.GetDepartmentInfoBatchRespVo, error) { 93 | queryParams := make([]http.QueryParameter, 0) 94 | if depIds != nil && len(depIds) > 0 { 95 | for _, id := range depIds { 96 | queryParams = append(queryParams, http.QueryParameter{ 97 | Key: "ids", 98 | Value: id, 99 | }) 100 | } 101 | } 102 | respBody, err := http.GetRepetition(consts.ApiDepartmentInfoBatchGet, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 103 | if err != nil { 104 | log.Error(err) 105 | return nil, err 106 | } 107 | respVo := &vo.GetDepartmentInfoBatchRespVo{} 108 | json.FromJsonIgnoreError(respBody, respVo) 109 | return respVo, nil 110 | } 111 | 112 | //获取部门用户列表 https://open.feishu.cn/document/ukTMukTMukTM/uEzNz4SM3MjLxczM 113 | func (t Tenant) GetDepartmentUserList(departmentId string, offset, pageSize int, fetchChild bool) (*vo.GetDepartmentUserListRespVo, error) { 114 | queryParams := map[string]interface{}{ 115 | "department_id": departmentId, 116 | "offset": offset, 117 | "page_size": pageSize, 118 | "fetch_child": fetchChild, 119 | } 120 | respBody, err := http.Get(consts.ApiDepartmentUserList, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 121 | if err != nil { 122 | log.Error(err) 123 | return nil, err 124 | } 125 | respVo := &vo.GetDepartmentUserListRespVo{} 126 | json.FromJsonIgnoreError(respBody, respVo) 127 | return respVo, nil 128 | } 129 | 130 | //获取部门用户列表 https://open.feishu.cn/document/ukTMukTMukTM/uEzNz4SM3MjLxczM 131 | func (t Tenant) GetDepartmentUserListV2(departmentId string, pageToken string, pageSize int, fetchChild bool) (*vo.GetDepartmentUserListV2RespVo, error) { 132 | queryParams := map[string]interface{}{ 133 | "id": departmentId, 134 | "fetch_child": fetchChild, 135 | } 136 | if pageToken != "" { 137 | queryParams["page_token"] = pageToken 138 | } 139 | if pageSize > 0 { 140 | queryParams["page_size"] = pageSize 141 | } 142 | 143 | respBody, err := http.Get(consts.ApiDepartmentUserListV2, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 144 | if err != nil { 145 | log.Error(err) 146 | return nil, err 147 | } 148 | respVo := &vo.GetDepartmentUserListV2RespVo{} 149 | json.FromJsonIgnoreError(respBody, respVo) 150 | return respVo, nil 151 | } 152 | 153 | //获取部门用户详情列表 https://open.feishu.cn/document/ukTMukTMukTM/uYzN3QjL2czN04iN3cDN?lang=zh-CN 154 | func (t Tenant) GetDepartmentUserDetailList(departmentId string, offset, pageSize int, fetchChild bool) (*vo.GetDepartmentUserDetailListRespVo, error) { 155 | queryParams := map[string]interface{}{ 156 | "department_id": departmentId, 157 | "offset": offset, 158 | "page_size": pageSize, 159 | "fetch_child": fetchChild, 160 | } 161 | respBody, err := http.Get(consts.ApiDepartmentUserDetailList, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 162 | if err != nil { 163 | log.Error(err) 164 | return nil, err 165 | } 166 | respVo := &vo.GetDepartmentUserDetailListRespVo{} 167 | json.FromJsonIgnoreError(respBody, respVo) 168 | return respVo, nil 169 | } 170 | 171 | //获取部门用户详情列表V2 https://open.feishu.cn/document/ukTMukTMukTM/uYzN3QjL2czN04iN3cDN?lang=zh-CN 172 | func (t Tenant) GetDepartmentUserDetailListV2(departmentId string, pageToken string, pageSize int, fetchChild bool) (*vo.GetUserBatchGetV2Resp, error) { 173 | queryParams := map[string]interface{}{ 174 | "id": departmentId, 175 | "fetch_child": fetchChild, 176 | } 177 | if pageToken != "" { 178 | queryParams["page_token"] = pageToken 179 | } 180 | if pageSize > 0 { 181 | queryParams["page_size"] = pageSize 182 | } 183 | respBody, err := http.Get(consts.ApiDepartmentUserDetailListV2, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 184 | if err != nil { 185 | log.Error(err) 186 | return nil, err 187 | } 188 | respVo := &vo.GetUserBatchGetV2Resp{} 189 | json.FromJsonIgnoreError(respBody, respVo) 190 | return respVo, nil 191 | } 192 | 193 | //批量获取用户信息 https://open.feishu.cn/document/ukTMukTMukTM/uIzNz4iM3MjLyczM 194 | func (t Tenant) GetUserBatchGet(employeeIds []string, openIds []string) (*vo.GetUserBatchGetRespVo, error) { 195 | queryParams := make([]http.QueryParameter, 0) 196 | if employeeIds != nil && len(employeeIds) > 0 { 197 | for _, id := range employeeIds { 198 | queryParams = append(queryParams, http.QueryParameter{ 199 | Key: "employee_ids", 200 | Value: id, 201 | }) 202 | } 203 | } 204 | if openIds != nil && len(openIds) > 0 { 205 | for _, id := range openIds { 206 | queryParams = append(queryParams, http.QueryParameter{ 207 | Key: "open_ids", 208 | Value: id, 209 | }) 210 | } 211 | } 212 | respBody, err := http.GetRepetition(consts.ApiUserBatchGet, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 213 | if err != nil { 214 | log.Error(err) 215 | return nil, err 216 | } 217 | respVo := &vo.GetUserBatchGetRespVo{} 218 | json.FromJsonIgnoreError(respBody, respVo) 219 | return respVo, nil 220 | } 221 | 222 | //批量获取用户信息 https://open.feishu.cn/document/ukTMukTMukTM/uIzNz4iM3MjLyczM 223 | func (t Tenant) GetUserBatchGetV2(employeeIds []string, openIds []string) (*vo.GetUserBatchGetV2Resp, error) { 224 | queryParams := make([]http.QueryParameter, 0) 225 | if employeeIds != nil && len(employeeIds) > 0 { 226 | for _, id := range employeeIds { 227 | queryParams = append(queryParams, http.QueryParameter{ 228 | Key: "employee_ids", 229 | Value: id, 230 | }) 231 | } 232 | } 233 | if openIds != nil && len(openIds) > 0 { 234 | for _, id := range openIds { 235 | queryParams = append(queryParams, http.QueryParameter{ 236 | Key: "open_ids", 237 | Value: id, 238 | }) 239 | } 240 | } 241 | respBody, err := http.GetRepetition(consts.ApiUserBatchGetV2, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 242 | if err != nil { 243 | log.Error(err) 244 | return nil, err 245 | } 246 | respVo := &vo.GetUserBatchGetV2Resp{} 247 | json.FromJsonIgnoreError(respBody, respVo) 248 | return respVo, nil 249 | } 250 | 251 | func (t Tenant) GetUsersV3(userIdType, departmentIdType, departmentId, pageToken string, pageSize int) (*vo.GetUsersV3Resp, error) { 252 | queryParams := map[string]interface{}{} 253 | if pageToken != "" { 254 | queryParams["page_token"] = pageToken 255 | } 256 | if pageSize > 0 { 257 | queryParams["page_size"] = pageSize 258 | } 259 | if userIdType != "" { 260 | queryParams["user_id_type"] = userIdType 261 | } 262 | if departmentIdType != "" { 263 | queryParams["department_id_type"] = departmentIdType 264 | } 265 | if departmentId != "" { 266 | queryParams["department_id"] = departmentId 267 | } 268 | respBody, err := http.Get(consts.ApiUserListV3, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 269 | if err != nil { 270 | log.Error(err) 271 | return nil, err 272 | } 273 | respVo := &vo.GetUsersV3Resp{} 274 | json.FromJsonIgnoreError(respBody, respVo) 275 | return respVo, nil 276 | } 277 | -------------------------------------------------------------------------------- /sdk/department_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 7 | "gotest.tools/assert" 8 | 9 | "testing" 10 | ) 11 | 12 | func TestTenant_GetScope(t *testing.T) { 13 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 14 | t.Log(e) 15 | t.Log(json.ToJsonIgnoreError(app)) 16 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 17 | t.Log(e) 18 | 19 | resp, err := tenant.GetScope() 20 | log.Info(json.ToJsonIgnoreError(resp), err) 21 | assert.Equal(t, err, nil) 22 | assert.Equal(t, resp.Code, 0) 23 | 24 | //openIds := resp.Data.AuthedOpenIds 25 | openIds := []string{"ou_b7d861c94cea4d316a6bfc5e8421994c"} 26 | resp1, err := tenant.GetUserBatchGet(nil, openIds) 27 | log.Info(json.ToJsonIgnoreError(resp1), err) 28 | //assert.Equal(t, err, nil) 29 | // //assert.Equal(t, resp1.Code, 0) 30 | 31 | t.Log(json.ToJsonIgnoreError(resp1)) 32 | 33 | resp2, err := tenant.GetUserBatchGetV2(nil, openIds) 34 | log.Info(json.ToJsonIgnoreError(resp2), err) 35 | assert.Equal(t, err, nil) 36 | assert.Equal(t, resp2.Code, 0) 37 | 38 | t.Log(json.ToJsonIgnoreError(resp2)) 39 | } 40 | 41 | func TestTenant_GetUserBatchGetV2(t *testing.T) { 42 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 43 | t.Log(e) 44 | t.Log(json.ToJsonIgnoreError(app)) 45 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 46 | t.Log(e) 47 | 48 | resp, err := tenant.GetUserBatchGetV2(nil, []string{"ou_14e6b0d968a56bdbbdd511f896400e3c", "ou_fb0c3442f0ec403cdd6971234088d975", "ou_0e40d4035ceb951467beb62bb1f3e5ba", "ou_dcff2de6a28060eff9f0d9665d2d28be", "ou_e1b43c426e884c586d52751853896688"}) 49 | log.Info(json.ToJsonIgnoreError(resp), err) 50 | assert.Equal(t, err, nil) 51 | assert.Equal(t, resp.Code, 0) 52 | } 53 | 54 | func TestTenant_GetScopeV2(t *testing.T) { 55 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 56 | t.Log(e) 57 | t.Log(json.ToJsonIgnoreError(app)) 58 | tenant, e := BuildTenant(app.AppAccessToken, "138337e75b0b575d") 59 | t.Log(e) 60 | 61 | resp, err := tenant.GetScopeV2() 62 | log.Info(json.ToJsonIgnoreError(resp), err) 63 | assert.Equal(t, err, nil) 64 | assert.Equal(t, resp.Code, 0) 65 | } 66 | 67 | func TestGetDepartmentSimpleList(t *testing.T) { 68 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 69 | t.Log(e) 70 | t.Log(json.ToJsonIgnoreError(app)) 71 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 72 | t.Log(e) 73 | 74 | resp, err := tenant.GetDepartmentSimpleList("0", 0, 100, true) 75 | log.Info(json.ToJsonIgnoreError(resp), err) 76 | assert.Equal(t, err, nil) 77 | assert.Equal(t, resp.Code, 0) 78 | } 79 | 80 | func TestTenant_GetDepartmentSimpleListV2(t *testing.T) { 81 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 82 | t.Log(e) 83 | t.Log(json.ToJsonIgnoreError(app)) 84 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 85 | t.Log(e) 86 | 87 | resp, err := tenant.GetDepartmentSimpleListV2("0", "", 100, true) 88 | log.Info(json.ToJsonIgnoreError(resp), err) 89 | assert.Equal(t, err, nil) 90 | assert.Equal(t, resp.Code, 0) 91 | t.Log(json.ToJsonIgnoreError(resp)) 92 | } 93 | 94 | func TestTenant_GetDepartmentInfo(t *testing.T) { 95 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 96 | t.Log(e) 97 | t.Log(json.ToJsonIgnoreError(app)) 98 | tenant, e := BuildTenant(app.AppAccessToken, "2ee83c762dcf1657") 99 | t.Log(e) 100 | 101 | resp, err := tenant.GetDepartmentInfo("g9gged32cca9e9fe") 102 | log.Info(json.ToJsonIgnoreError(resp), err) 103 | assert.Equal(t, err, nil) 104 | assert.Equal(t, resp.Code, 0) 105 | } 106 | 107 | func TestTenant_GetDepartmentUserList(t *testing.T) { 108 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 109 | t.Log(e) 110 | t.Log(json.ToJsonIgnoreError(app)) 111 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 112 | t.Log(e) 113 | 114 | resp, err := tenant.GetDepartmentUserList("0", 0, 100, true) 115 | log.Info(json.ToJsonIgnoreError(resp), err) 116 | assert.Equal(t, err, nil) 117 | assert.Equal(t, resp.Code, 0) 118 | } 119 | 120 | func TestTenant_GetDepartmentInfoBatch(t *testing.T) { 121 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 122 | t.Log(e) 123 | t.Log(json.ToJsonIgnoreError(app)) 124 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 125 | t.Log(e) 126 | 127 | resp, err := tenant.GetDepartmentSimpleListV2("0", "", 100, true) 128 | log.Info(json.ToJsonIgnoreError(resp), err) 129 | assert.Equal(t, err, nil) 130 | assert.Equal(t, resp.Code, 0) 131 | t.Log(json.ToJsonIgnoreError(resp)) 132 | 133 | deps := make([]string, 0) 134 | for _, dep := range resp.Data.DepartmentInfos { 135 | deps = append(deps, dep.Id) 136 | } 137 | resp1, err := tenant.GetDepartmentInfoBatch(deps) 138 | log.Info(json.ToJsonIgnoreError(resp1), err) 139 | assert.Equal(t, err, nil) 140 | assert.Equal(t, resp1.Code, 0) 141 | t.Log(json.ToJsonIgnoreError(resp1)) 142 | 143 | } 144 | 145 | func TestTenant_GetDepartmentUserV2List(t *testing.T) { 146 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 147 | t.Log(e) 148 | t.Log(json.ToJsonIgnoreError(app)) 149 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 150 | t.Log(e) 151 | 152 | resp, err := tenant.GetDepartmentUserListV2("0", "", 100, true) 153 | log.Info(json.ToJsonIgnoreError(resp), err) 154 | assert.Equal(t, err, nil) 155 | assert.Equal(t, resp.Code, 0) 156 | } 157 | 158 | func TestTenant_GetDepartmentUserDetailList(t *testing.T) { 159 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 160 | t.Log(e) 161 | t.Log(json.ToJsonIgnoreError(app)) 162 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 163 | t.Log(e) 164 | 165 | resp, err := tenant.GetDepartmentSimpleList("0", 0, 100, true) 166 | log.Info(json.ToJsonIgnoreError(resp), err) 167 | assert.Equal(t, err, nil) 168 | assert.Equal(t, resp.Code, 0) 169 | 170 | for _, dep := range resp.Data.DepartmentInfos { 171 | resp1, err := tenant.GetDepartmentUserDetailList(dep.Id, 0, 100, true) 172 | log.Info(json.ToJsonIgnoreError(resp1), err) 173 | assert.Equal(t, err, nil) 174 | assert.Equal(t, resp1.Code, 0) 175 | } 176 | 177 | } 178 | 179 | func TestTenant_GetDepartmentUserDetailListV2(t *testing.T) { 180 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 181 | t.Log(e) 182 | t.Log(json.ToJsonIgnoreError(app)) 183 | tenant, e := BuildTenant(app.AppAccessToken, "2ee83c762dcf1657") 184 | t.Log(e) 185 | 186 | resp1, err := tenant.GetDepartmentUserDetailListV2("od-938643fa45292b9906968f49857ebe18", "763bd1e74d05ea7c", 100, false) 187 | log.Info(json.ToJsonIgnoreError(resp1), err) 188 | assert.Equal(t, err, nil) 189 | assert.Equal(t, resp1.Code, 0) 190 | t.Log(json.ToJsonIgnoreError(resp1)) 191 | //resp, err := tenant.GetDepartmentSimpleList("0", 0, 100, true) 192 | //log.Info(json.ToJsonIgnoreError(resp), err) 193 | //assert.Equal(t, err, nil) 194 | //assert.Equal(t, resp.Code, 0) 195 | // 196 | //for _, dep := range resp.Data.DepartmentInfos { 197 | // resp1, err := tenant.GetDepartmentUserDetailListV2(dep.Id, "", 100, true) 198 | // log.Info(json.ToJsonIgnoreError(resp1), err) 199 | // assert.Equal(t, err, nil) 200 | // assert.Equal(t, resp1.Code, 0) 201 | // t.Log(json.ToJsonIgnoreError(resp1)) 202 | //} 203 | 204 | } 205 | 206 | func TestTenant_GetUsersV3(t *testing.T) { 207 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 208 | if e != nil { 209 | t.Fatal(e) 210 | } 211 | t.Log(json.ToJsonIgnoreError(app)) 212 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 213 | if e != nil { 214 | t.Fatal(e) 215 | } 216 | t.Log(e) 217 | resp, e := tenant.GetUsersV3("", "", "", "", 100) 218 | if e != nil { 219 | t.Fatal(e) 220 | } 221 | for _, user := range resp.Data.Items { 222 | t.Log(json.ToJsonIgnoreError(user.DepartmentIds)) 223 | } 224 | t.Log(json.ToJsonIgnoreError(resp)) 225 | } 226 | -------------------------------------------------------------------------------- /sdk/document.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //文档搜索 https://open.feishu.cn/document/ugTM5UjL4ETO14COxkTN/ugDM4UjL4ADO14COwgTN 12 | func (t Tenant) SearchDocs(userAccessToken string, req vo.SearchDocsReqVo) (*vo.SearchDocsRespVo, error) { 13 | respBody, err := http.Post(consts.ApiSearchDocs, nil, json.ToJsonIgnoreError(req), http.BuildTokenHeaderOptions(userAccessToken)) 14 | if err != nil { 15 | log.Error(err) 16 | return nil, err 17 | } 18 | respVo := &vo.SearchDocsRespVo{} 19 | json.FromJsonIgnoreError(respBody, respVo) 20 | return respVo, nil 21 | } 22 | 23 | //获取文档元信息 https://open.feishu.cn/document/ugTM5UjL4ETO14COxkTN/uczN3UjL3czN14yN3cTN 24 | func (t Tenant) GetDocMeta(userAccessToken string, docToken string) (*vo.GetDocMetaRespVo, error) { 25 | respBody, err := http.Get(consts.ApiGetDocMeta+"/"+docToken, nil, http.BuildTokenHeaderOptions(userAccessToken)) 26 | if err != nil { 27 | log.Error(err) 28 | return nil, err 29 | } 30 | respVo := &vo.GetDocMetaRespVo{} 31 | json.FromJsonIgnoreError(respBody, respVo) 32 | return respVo, nil 33 | } 34 | -------------------------------------------------------------------------------- /sdk/document_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestTenant_SearchDocs(t *testing.T) { 13 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 14 | t.Log(e) 15 | t.Log(json.ToJsonIgnoreError(app)) 16 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 17 | t.Log(e) 18 | //a := int(50) 19 | resp, err := tenant.SearchDocs("u-KaKuv3qHfriTjs2LI0wTah", vo.SearchDocsReqVo{ 20 | SearchKey: nil, 21 | //Count: &a, 22 | //Offset: &a, 23 | OwnerIds: nil, 24 | ChatIds: nil, 25 | //DocsTypes: &[]string{"doc", "file}, 26 | }) 27 | log.Info(json.ToJsonIgnoreError(resp), err) 28 | assert.Equal(t, err, nil) 29 | assert.Equal(t, resp.Code, 0) 30 | } 31 | 32 | func TestTenant_GetDocMeta(t *testing.T) { 33 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 34 | t.Log(e) 35 | t.Log(json.ToJsonIgnoreError(app)) 36 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 37 | t.Log(e) 38 | resp, err := tenant.GetDocMeta("u-b2pimsbPjM7n6Q68L2quma", "shtcnXthMbCHWH5SPd3GGOiMVpg") 39 | log.Info(json.ToJsonIgnoreError(resp), err) 40 | assert.Equal(t, err, nil) 41 | assert.Equal(t, resp.Code, 0) 42 | } 43 | -------------------------------------------------------------------------------- /sdk/image.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 7 | http2 "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | "io" 10 | "io/ioutil" 11 | "mime/multipart" 12 | "net/http" 13 | "os" 14 | "unsafe" 15 | ) 16 | 17 | func (t Tenant) NewFileUploadRequest(uri string, params map[string]string, paramName, path string) error { 18 | file, err := os.Open(path) 19 | if err != nil { 20 | return err 21 | } 22 | defer file.Close() 23 | body := &bytes.Buffer{} 24 | writer := multipart.NewWriter(body) 25 | part, err := writer.CreateFormFile(paramName, path) 26 | if err != nil { 27 | return err 28 | } 29 | _, err = io.Copy(part, file) 30 | for key, val := range params { 31 | _ = writer.WriteField(key, val) 32 | } 33 | writer.WriteField("image_type", "message") 34 | err = writer.Close() 35 | if err != nil { 36 | return err 37 | } 38 | request, err := http.NewRequest("POST", uri, body) 39 | request.Header.Set("Content-Type", writer.FormDataContentType()) 40 | request.Header.Set("Authorization", "Bearer "+t.TenantAccessToken) 41 | client := http.Client{} 42 | resp, err := client.Do(request) 43 | if err != nil { 44 | fmt.Println(err.Error()) 45 | } 46 | respBytes, err := ioutil.ReadAll(resp.Body) 47 | if err != nil { 48 | fmt.Println(err.Error()) 49 | return err 50 | } 51 | str := (*string)(unsafe.Pointer(&respBytes)) 52 | fmt.Println(*str) 53 | return err 54 | } 55 | 56 | //func (t Tenant) GetImage(imageKey string) ([]byte, error) { 57 | func (t Tenant) GetImage(imageKey string, isApp bool) (io.ReadCloser, error) { 58 | queryParams := map[string]interface{}{} 59 | queryParams["image_key"] = imageKey 60 | if isApp { 61 | queryParams["operator"] = "app" 62 | } 63 | request, err := http.NewRequest("GET", consts.ApiGetImage+http2.ConvertToQueryParams(queryParams), nil) 64 | if err != nil { 65 | log.Error(err) 66 | return nil, err 67 | } 68 | request.Header.Set("Authorization", "Bearer "+t.TenantAccessToken) 69 | client := http.Client{} 70 | resp, err := client.Do(request) 71 | if err != nil { 72 | log.Error(err) 73 | return nil, err 74 | } 75 | 76 | //respBytes, err := ioutil.ReadAll(resp.Body) 77 | //if err != nil { 78 | // log.Error(err) 79 | // return nil, err 80 | //} 81 | 82 | return resp.Body, nil 83 | } 84 | -------------------------------------------------------------------------------- /sdk/image_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "github.com/aliyun/aliyun-oss-go-sdk/oss" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 9 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 10 | uuid "github.com/satori/go.uuid" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "testing" 16 | ) 17 | 18 | var fileTypeMap sync.Map 19 | 20 | func TestTenant_NewFileUploadRequest(t *testing.T) { 21 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 22 | t.Log(e) 23 | t.Log(json.ToJsonIgnoreError(app)) 24 | tenant, e := BuildTenant(app.AppAccessToken, "2c29657237ce175d") 25 | t.Log(e) 26 | 27 | //t.Log(NewFileUploadRequest("https://open.feishu.cn/open-apis/image/v4/put/", 28 | // data, "image", "C:\\Users\\admin\\Desktop\\11111.png")) 29 | 30 | data := make(map[string]string) 31 | resp := tenant.NewFileUploadRequest("https://open.feishu.cn/open-apis/image/v4/put/", data, "image", "C:\\Users\\admin\\Desktop\\12345.png") 32 | t.Log(json.ToJsonIgnoreError(resp)) 33 | } 34 | 35 | func TestTenant_GetImage(t *testing.T) { 36 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 37 | t.Log(e) 38 | t.Log(json.ToJsonIgnoreError(app)) 39 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 40 | t.Log(e) 41 | 42 | //resp, err := tenant.GetImage("img_b2092abb-5a13-4e68-bd04-fb70da58227g", false) 43 | resp, err := tenant.GetImage("img_ead2e693-606e-40a0-a979-80243a19ee8g", false) 44 | //resp, err := tenant.GetImage("img_f9713733-cb19-4a4b-88f8-7d5a3937b5ag", false) 45 | t.Log(err) 46 | t.Log(json.ToJsonIgnoreError(resp)) 47 | 48 | // 创建OSSClient实例。 49 | client, err := oss.New("xxxxxx", "xxxxx", "xxxxxxx") 50 | if err != nil { 51 | fmt.Println("Error1:", err) 52 | os.Exit(-1) 53 | } 54 | 55 | // 获取存储空间。 56 | bucket, err := client.Bucket("polaris-hd2") 57 | if err != nil { 58 | fmt.Println("Error2:", err) 59 | os.Exit(-1) 60 | } 61 | 62 | //var nextPos int64 = 0 63 | //objName := "test/test28.png" 64 | //defer resp.Close() 65 | ////分片逐步写入 66 | //buf := make([]byte, 40960) 67 | //for { 68 | // n, err := resp.Read(buf) 69 | // if err != nil { 70 | // break 71 | // } 72 | // 73 | // nextPos, err = bucket.AppendObject(objName, bytes.NewReader(buf[:n]), nextPos) 74 | // if err != nil { 75 | // fmt.Println("Error:", err) 76 | // os.Exit(-1) 77 | // } 78 | //} 79 | 80 | var nextPos int64 = 0 81 | objName := "test/" + NewUuid() 82 | fileType := "" 83 | defer resp.Close() 84 | //分片逐步写入 85 | buf := make([]byte, 1024) 86 | i := 0 87 | for { 88 | n, err := resp.Read(buf) 89 | if err != nil { 90 | break 91 | } 92 | if i == 0 { 93 | fileType = GetFileType(buf[:n]) 94 | fmt.Println("fileType:", buf[:n]) 95 | if fileType != "" { 96 | objName = objName + "." + fileType 97 | } 98 | } 99 | i++ 100 | fmt.Println(i) 101 | 102 | nextPos, err = bucket.AppendObject(objName, bytes.NewReader(buf[:n]), nextPos) 103 | if err != nil { 104 | fmt.Println("Error:", err) 105 | os.Exit(-1) 106 | } 107 | } 108 | t.Log(objName) 109 | 110 | //// 第一次追加上传的位置是0,返回值为下一次追加的位置。后续追加的位置是追加前文件的长度。 111 | //// 填写不包含Bucket名称在内的Object的完整路径,例如example/test.txt。 112 | //nextPos, err = bucket.AppendObject(objName, strings.NewReader("YourObjectAppendValue1"), nextPos) 113 | //if err != nil { 114 | // fmt.Println("Error:", err) 115 | // os.Exit(-1) 116 | //} 117 | // 118 | //// 第二次追加上传。 119 | //nextPos, err = bucket.AppendObject(objName, strings.NewReader("YourObjectAppendValue2"), nextPos) 120 | //if err != nil { 121 | // fmt.Println("Error:", err) 122 | // os.Exit(-1) 123 | //} 124 | 125 | } 126 | 127 | func NewUuid() string { 128 | id := uuid.NewV4() 129 | return strings.ReplaceAll(id.String(), "-", "") 130 | } 131 | 132 | //4749463839612c013002f400007a7a 133 | //47494638396137023702f644 134 | 135 | // 用文件前面几个字节来判断 136 | // fSrc: 文件字节流(就用前面几个字节) 137 | func GetFileType(fSrc []byte) string { 138 | //fileTypeMap.Store("ffd8ffe000104a464946", "jpg") //JPEG (jpg) 139 | fileTypeMap.Store("ffd8", "jpg") //JPEG (jpg) 140 | //fileTypeMap.Store("89504e470d0a1a0a0000", "png") //PNG (png) 141 | fileTypeMap.Store("89504e", "png") //PNG (png) 142 | fileTypeMap.Store("47494638396126026f01", "gif") //GIF (gif) 143 | //fileTypeMap.Store("49492a00227105008037", "tif") //TIFF (tif) 144 | fileTypeMap.Store("49492a", "tif") //TIFF (tif) 145 | fileTypeMap.Store("424d228c010000000000", "bmp") //16色位图(bmp) 146 | fileTypeMap.Store("424d8240090000000000", "bmp") //24位位图(bmp) 147 | fileTypeMap.Store("424d8e1b030000000000", "bmp") //256色位图(bmp) 148 | fileTypeMap.Store("41433130313500000000", "dwg") //CAD (dwg) 149 | fileTypeMap.Store("3c21444f435459504520", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0 150 | fileTypeMap.Store("3c68746d6c3e0", "html") //HTML (html) 3c68746d6c3e0 3c68746d6c3e0 151 | fileTypeMap.Store("3c21646f637479706520", "htm") //HTM (htm) 152 | fileTypeMap.Store("48544d4c207b0d0a0942", "css") //css 153 | fileTypeMap.Store("696b2e71623d696b2e71", "js") //js 154 | fileTypeMap.Store("7b5c727466315c616e73", "rtf") //Rich Text Format (rtf) 155 | //fileTypeMap.Store("38425053000100000000", "psd") //Photoshop (psd) 156 | fileTypeMap.Store("384250", "psd") //Photoshop (psd) 157 | fileTypeMap.Store("46726f6d3a203d3f6762", "eml") //Email [Outlook Express 6] (eml) 158 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "doc") //MS Excel 注意:word、msi 和 excel的文件头一样 159 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "vsd") //Visio 绘图 160 | fileTypeMap.Store("5374616E64617264204A", "mdb") //MS Access (mdb) 161 | fileTypeMap.Store("252150532D41646F6265", "ps") 162 | fileTypeMap.Store("255044", "pdf") //Adobe Acrobat (pdf) 163 | //fileTypeMap.Store("255044462d312e350d0a", "pdf") //Adobe Acrobat (pdf) 164 | fileTypeMap.Store("2e524d46000000120001", "rmvb") //rmvb/rm相同 165 | fileTypeMap.Store("464c5601050000000900", "flv") //flv与f4v相同 166 | fileTypeMap.Store("00000020667479706d70", "mp4") 167 | fileTypeMap.Store("49443303000000002176", "mp3") 168 | fileTypeMap.Store("000001ba210001000180", "mpg") // 169 | fileTypeMap.Store("3026b2758e66cf11a6d9", "wmv") //wmv与asf相同 170 | fileTypeMap.Store("52494646e27807005741", "wav") //Wave (wav) 171 | fileTypeMap.Store("52494646d07d60074156", "avi") 172 | fileTypeMap.Store("4d546864000000060001", "mid") //MIDI (mid) 173 | fileTypeMap.Store("504b0304140000000800", "zip") 174 | fileTypeMap.Store("526172211a0700cf9073", "rar") 175 | fileTypeMap.Store("235468697320636f6e66", "ini") 176 | fileTypeMap.Store("504b03040a0000000000", "jar") 177 | fileTypeMap.Store("4d5a9000030000000400", "exe") //可执行文件 178 | fileTypeMap.Store("3c25402070616765206c", "jsp") //jsp文件 179 | fileTypeMap.Store("4d616e69666573742d56", "mf") //MF文件 180 | fileTypeMap.Store("3c3f786d6c2076657273", "xml") //xml文件 181 | fileTypeMap.Store("494e5345525420494e54", "sql") //xml文件 182 | fileTypeMap.Store("7061636b616765207765", "java") //java文件 183 | fileTypeMap.Store("406563686f206f66660d", "bat") //bat文件 184 | fileTypeMap.Store("1f8b0800000000000000", "gz") //gz文件 185 | fileTypeMap.Store("6c6f67346a2e726f6f74", "properties") //bat文件 186 | fileTypeMap.Store("cafebabe0000002e0041", "class") //bat文件 187 | fileTypeMap.Store("49545346030000006000", "chm") //bat文件 188 | fileTypeMap.Store("04000000010000001300", "mxp") //bat文件 189 | fileTypeMap.Store("504b0304140006000800", "docx") //docx文件 190 | fileTypeMap.Store("d0cf11e0a1b11ae10000", "wps") //WPS文字wps、表格et、演示dps都是一样的 191 | fileTypeMap.Store("6431303a637265617465", "torrent") 192 | fileTypeMap.Store("6D6F6F76", "mov") //Quicktime (mov) 193 | fileTypeMap.Store("FF575043", "wpd") //WordPerfect (wpd) 194 | fileTypeMap.Store("CFAD12FEC5FD746F", "dbx") //Outlook Express (dbx) 195 | fileTypeMap.Store("2142444E", "pst") //Outlook (pst) 196 | fileTypeMap.Store("AC9EBD8F", "qdf") //Quicken (qdf) 197 | fileTypeMap.Store("E3828596", "pwl") //Windows Password (pwl) 198 | fileTypeMap.Store("2E7261FD", "ram") //Real Audio (ram) 199 | 200 | var fileType string 201 | fileCode := bytesToHexString(fSrc) 202 | fmt.Println(fileCode) 203 | 204 | fileTypeMap.Range(func(key, value interface{}) bool { 205 | k := key.(string) 206 | v := value.(string) 207 | if strings.HasPrefix(fileCode, strings.ToLower(k)) || 208 | strings.HasPrefix(k, strings.ToLower(fileCode)) { 209 | fileType = v 210 | return false 211 | } 212 | return true 213 | }) 214 | return fileType 215 | } 216 | 217 | // 获取前面结果字节的二进制 218 | func bytesToHexString(src []byte) string { 219 | res := bytes.Buffer{} 220 | if src == nil || len(src) <= 0 { 221 | return "" 222 | } 223 | temp := make([]byte, 0) 224 | for _, v := range src { 225 | sub := v & 0xFF 226 | hv := hex.EncodeToString(append(temp, sub)) 227 | if len(hv) < 2 { 228 | res.WriteString(strconv.FormatInt(int64(0), 10)) 229 | } 230 | res.WriteString(hv) 231 | } 232 | return res.String() 233 | } 234 | 235 | func TestBuildInternalApp(t *testing.T) { 236 | // 创建OSSClient实例。 237 | client, err := oss.New("", "", "") 238 | if err != nil { 239 | fmt.Println("Error:", err) 240 | os.Exit(-1) 241 | } 242 | 243 | bucketName := "" 244 | 245 | // 获取存储空间。 246 | bucket, err := client.Bucket(bucketName) 247 | if err != nil { 248 | fmt.Println("Error:", err) 249 | os.Exit(-1) 250 | } 251 | var nextPos int64 = 0 252 | // 第一次追加上传的位置是0,返回值为下一次追加的位置。后续追加的位置是追加前文件的长度。 253 | // 填写不包含Bucket名称在内的Object的完整路径,例如example/test.txt。 254 | nextPos, err = bucket.AppendObject("", strings.NewReader("YourObjectAppendValue1"), nextPos) 255 | if err != nil { 256 | fmt.Println("Error:", err) 257 | os.Exit(-1) 258 | } 259 | 260 | // 如果不是第一次追加上传,可以通过bucket.GetObjectDetailedMeta方法或上次追加返回值的X-Oss-Next-Append-Position的属性,获取追加位置。 261 | //props, err := bucket.GetObjectDetailedMeta("") 262 | //if err != nil { 263 | // fmt.Println("Error:", err) 264 | // os.Exit(-1) 265 | //} 266 | //nextPos, err = strconv.ParseInt(props.Get("X-Oss-Next-Append-Position"), 10, 64) 267 | //if err != nil { 268 | // fmt.Println("Error:", err) 269 | // os.Exit(-1) 270 | //} 271 | 272 | // 第二次追加上传。 273 | nextPos, err = bucket.AppendObject("", strings.NewReader("YourObjectAppendValue2"), nextPos) 274 | if err != nil { 275 | fmt.Println("Error:", err) 276 | os.Exit(-1) 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /sdk/order.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //查询用户是否在应用开通范围 https://open.feishu.cn/document/ukTMukTMukTM/uATNwUjLwUDM14CM1ATN 12 | func (t Tenant) CheckUser(req vo.CheckUserReq) (*vo.CheckUserResp, error) { 13 | queryParams := map[string]interface{}{} 14 | if req.UserId != "" { 15 | queryParams["user_id"] = req.UserId 16 | } 17 | if req.OpenId != "" { 18 | queryParams["open_id"] = req.OpenId 19 | } 20 | respBody, err := http.Get(consts.ApiCheckUser, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 21 | if err != nil { 22 | log.Error(err) 23 | return nil, err 24 | } 25 | respVo := &vo.CheckUserResp{} 26 | json.FromJsonIgnoreError(respBody, respVo) 27 | return respVo, nil 28 | } 29 | 30 | //查询租户购买的付费方案 https://open.feishu.cn/document/ukTMukTMukTM/uETNwUjLxUDM14SM1ATN 31 | func (t Tenant) GetOrderList(req vo.GetOrderListReq) (*vo.GetOrderListResp, error) { 32 | queryParams := map[string]interface{}{} 33 | queryParams["page_size"] = req.PageSize 34 | if req.Status != nil { 35 | queryParams["status"] = req.Status 36 | } 37 | if req.PageToken != nil { 38 | queryParams["page_token"] = req.PageToken 39 | } 40 | if req.TenantKey != nil { 41 | queryParams["tenant_key"] = req.TenantKey 42 | } 43 | respBody, err := http.Get(consts.ApiGetOrderList, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 44 | if err != nil { 45 | log.Error(err) 46 | return nil, err 47 | } 48 | respVo := &vo.GetOrderListResp{} 49 | json.FromJsonIgnoreError(respBody, respVo) 50 | return respVo, nil 51 | } 52 | 53 | //查询订单详情 https://open.feishu.cn/document/ukTMukTMukTM/uITNwUjLyUDM14iM1ATN 54 | func (t Tenant) GetOrderInfo(orderId string) (*vo.OrderInfoResp, error) { 55 | queryParams := map[string]interface{}{} 56 | queryParams["order_id"] = orderId 57 | respBody, err := http.Get(consts.ApiGetOrderInfo, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 58 | if err != nil { 59 | log.Error(err) 60 | return nil, err 61 | } 62 | respVo := &vo.OrderInfoResp{} 63 | json.FromJsonIgnoreError(respBody, respVo) 64 | return respVo, nil 65 | } 66 | -------------------------------------------------------------------------------- /sdk/order_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestTenant_CheckUser(t *testing.T) { 13 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 14 | t.Log(e) 15 | t.Log(json.ToJsonIgnoreError(app)) 16 | tenant, e := BuildTenant(app.AppAccessToken, "2deaa88e790f575e") 17 | t.Log(e) 18 | 19 | resp, err := tenant.CheckUser(vo.CheckUserReq{ 20 | OpenId: "ou_53681e85d8dfae37c76849145c3cf98f", 21 | //UserId: "", 22 | }) 23 | log.Info(json.ToJsonIgnoreError(resp), err) 24 | assert.Equal(t, err, nil) 25 | assert.Equal(t, resp.Code, 0) 26 | } 27 | 28 | func TestTenant_GetOrderList(t *testing.T) { 29 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 30 | t.Log(e) 31 | t.Log(json.ToJsonIgnoreError(app)) 32 | tenant, e := BuildTenant(app.AppAccessToken, "2deaa88e790f575e") 33 | t.Log(e) 34 | 35 | resp, err := tenant.GetOrderList(vo.GetOrderListReq{ 36 | PageSize: 10, 37 | }) 38 | log.Info(json.ToJsonIgnoreError(resp), err) 39 | assert.Equal(t, err, nil) 40 | assert.Equal(t, resp.Code, 0) 41 | } 42 | 43 | func TestTenant_GetOrderInfo(t *testing.T) { 44 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 45 | t.Log(e) 46 | t.Log(json.ToJsonIgnoreError(app)) 47 | tenant, e := BuildTenant(app.AppAccessToken, "2deaa88e790f575e") 48 | t.Log(e) 49 | 50 | resp, err := tenant.GetOrderInfo("6833281668439097346") 51 | log.Info(json.ToJsonIgnoreError(resp), err) 52 | assert.Equal(t, err, nil) 53 | assert.Equal(t, resp.Code, 0) 54 | } 55 | -------------------------------------------------------------------------------- /sdk/robot_chat.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //创建群 https://open.feishu.cn/document/ukTMukTMukTM/ukDO5QjL5gTO04SO4kDN 12 | func (t Tenant) CreateChat(msg vo.CreateChatReqVo) (*vo.CreateChatRespVo, error) { 13 | reqBody := json.ToJsonIgnoreError(msg) 14 | respBody, err := http.Post(consts.ApiCreateChat, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 15 | if err != nil { 16 | log.Error(err) 17 | return nil, err 18 | } 19 | respVo := &vo.CreateChatRespVo{} 20 | json.FromJsonIgnoreError(respBody, respVo) 21 | return respVo, nil 22 | } 23 | 24 | //获取群列表 https://open.feishu.cn/document/ukTMukTMukTM/uITO5QjLykTO04iM5kDN 25 | func (t Tenant) ChatList(pageSize int, pageToken string) (*vo.GroupListRespVo, error) { 26 | queryParams := map[string]interface{}{} 27 | if pageSize > 0 { 28 | queryParams["page_size"] = pageSize 29 | } 30 | if pageToken != "" { 31 | queryParams["page_token"] = pageToken 32 | } 33 | respBody, err := http.Get(consts.ApiChatList, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 34 | if err != nil { 35 | log.Error(err) 36 | return nil, err 37 | } 38 | respVo := &vo.GroupListRespVo{} 39 | json.FromJsonIgnoreError(respBody, respVo) 40 | return respVo, nil 41 | } 42 | 43 | //获取群信息 https://open.feishu.cn/document/ukTMukTMukTM/uMTO5QjLzkTO04yM5kDN 44 | func (t Tenant) ChatInfo(chatId string) (*vo.ChatInfoRespVo, error) { 45 | queryParams := map[string]interface{}{ 46 | "chat_id": chatId, 47 | } 48 | 49 | respBody, err := http.Get(consts.ApiChatInfo, queryParams, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 50 | if err != nil { 51 | log.Error(err) 52 | return nil, err 53 | } 54 | respVo := &vo.ChatInfoRespVo{} 55 | json.FromJsonIgnoreError(respBody, respVo) 56 | return respVo, nil 57 | } 58 | 59 | //更新群信息 https://open.feishu.cn/document/ukTMukTMukTM/uYTO5QjL2kTO04iN5kDN 60 | func (t Tenant) UpdateChat(msg vo.UpdateChatReqVo) (*vo.UpdateChatRespVo, error) { 61 | reqBody := json.ToJsonIgnoreError(msg) 62 | respBody, err := http.Post(consts.ApiUpdateChat, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 63 | if err != nil { 64 | log.Error(err) 65 | return nil, err 66 | } 67 | respVo := &vo.UpdateChatRespVo{} 68 | json.FromJsonIgnoreError(respBody, respVo) 69 | return respVo, nil 70 | } 71 | 72 | //拉用户进群 https://open.feishu.cn/document/ukTMukTMukTM/ucTO5QjL3kTO04yN5kDN 73 | func (t Tenant) AddChatUser(msg vo.UpdateChatMemberReqVo) (*vo.UpdateChatMemberRespVo, error) { 74 | reqBody := json.ToJsonIgnoreError(msg) 75 | respBody, err := http.Post(consts.ApiAddChatUser, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 76 | if err != nil { 77 | log.Error(err) 78 | return nil, err 79 | } 80 | respVo := &vo.UpdateChatMemberRespVo{} 81 | json.FromJsonIgnoreError(respBody, respVo) 82 | return respVo, nil 83 | } 84 | 85 | //移除用户出群 https://open.feishu.cn/document/ukTMukTMukTM/uADMwUjLwADM14CMwATN 86 | func (t Tenant) RemoveChatUser(msg vo.UpdateChatMemberReqVo) (*vo.UpdateChatMemberRespVo, error) { 87 | reqBody := json.ToJsonIgnoreError(msg) 88 | respBody, err := http.Post(consts.ApiRemoveChatUser, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 89 | if err != nil { 90 | log.Error(err) 91 | return nil, err 92 | } 93 | respVo := &vo.UpdateChatMemberRespVo{} 94 | json.FromJsonIgnoreError(respBody, respVo) 95 | return respVo, nil 96 | } 97 | 98 | //解散群 https://open.feishu.cn/document/ukTMukTMukTM/uUDN5QjL1QTO04SN0kDN 99 | func (t Tenant) DisbandChat(msg vo.UpdateChatData) (*vo.CommonVo, error) { 100 | reqBody := json.ToJsonIgnoreError(msg) 101 | respBody, err := http.Post(consts.ApiDisbandChat, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 102 | if err != nil { 103 | log.Error(err) 104 | return nil, err 105 | } 106 | respVo := &vo.CommonVo{} 107 | json.FromJsonIgnoreError(respBody, respVo) 108 | return respVo, nil 109 | } 110 | 111 | //拉机器人进群 https://open.feishu.cn/document/ukTMukTMukTM/uYDO04iN4QjL2gDN 112 | func (t Tenant) AddBot(msg vo.UpdateChatData) (*vo.CommonVo, error) { 113 | reqBody := json.ToJsonIgnoreError(msg) 114 | respBody, err := http.Post(consts.ApiAddBot, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 115 | if err != nil { 116 | log.Error(err) 117 | return nil, err 118 | } 119 | respVo := &vo.CommonVo{} 120 | json.FromJsonIgnoreError(respBody, respVo) 121 | return respVo, nil 122 | } 123 | -------------------------------------------------------------------------------- /sdk/robot_chat_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestTenant_CreateChat(t *testing.T) { 13 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 14 | t.Log(e) 15 | t.Log(json.ToJsonIgnoreError(app)) 16 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 17 | t.Log(e) 18 | 19 | resp, err := tenant.CreateChat(vo.CreateChatReqVo{ 20 | Name: "test", 21 | Description: "hhhh", 22 | OpenIds: []string{ 23 | "ou_433f2b1abf3e0ec316fd9e60d4cda654", 24 | "ou_87f1b2210acad10a90cc3690802626d7", 25 | }, 26 | }) 27 | log.Info(json.ToJsonIgnoreError(resp), err) 28 | assert.Equal(t, err, nil) 29 | assert.Equal(t, resp.Code, 0) 30 | } 31 | 32 | func TestTenant_ChatList(t *testing.T) { 33 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 34 | t.Log(e) 35 | t.Log(json.ToJsonIgnoreError(app)) 36 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 37 | t.Log(e) 38 | 39 | resp, err := tenant.ChatList(0, "") 40 | log.Info(json.ToJsonIgnoreError(resp), err) 41 | assert.Equal(t, err, nil) 42 | assert.Equal(t, resp.Code, 0) 43 | } 44 | 45 | func TestTenant_ChatInfo(t *testing.T) { 46 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 47 | t.Log(e) 48 | t.Log(json.ToJsonIgnoreError(app)) 49 | tenant, e := BuildTenant(app.AppAccessToken, "1319b042200f575e") 50 | t.Log(e) 51 | 52 | resp, err := tenant.ChatInfo("oc_4c1e5c42e6183294e72eba6c9475512d") 53 | log.Info(json.ToJsonIgnoreError(resp), err) 54 | assert.Equal(t, err, nil) 55 | assert.Equal(t, resp.Code, 0) 56 | } 57 | 58 | func TestTenant_UpdateChat(t *testing.T) { 59 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 60 | t.Log(e) 61 | t.Log(json.ToJsonIgnoreError(app)) 62 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 63 | t.Log(e) 64 | 65 | resp, err := tenant.UpdateChat(vo.UpdateChatReqVo{ 66 | ChatId: "", 67 | OwnerUserId: "", 68 | OwnerOpenId: "", 69 | Name: "", 70 | I18nNames: nil, 71 | }) 72 | log.Info(json.ToJsonIgnoreError(resp), err) 73 | assert.Equal(t, err, nil) 74 | assert.Equal(t, resp.Code, 0) 75 | } 76 | 77 | func TestTenant_AddChatUser(t *testing.T) { 78 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 79 | t.Log(e) 80 | t.Log(json.ToJsonIgnoreError(app)) 81 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 82 | t.Log(e) 83 | 84 | resp, err := tenant.AddChatUser(vo.UpdateChatMemberReqVo{ 85 | ChatId: "", 86 | UserIds: nil, 87 | OpenIds: nil, 88 | }) 89 | log.Info(json.ToJsonIgnoreError(resp), err) 90 | assert.Equal(t, err, nil) 91 | assert.Equal(t, resp.Code, 0) 92 | 93 | resp1, err1 := tenant.RemoveChatUser(vo.UpdateChatMemberReqVo{ 94 | ChatId: "", 95 | UserIds: nil, 96 | OpenIds: nil, 97 | }) 98 | log.Info(json.ToJsonIgnoreError(resp1), err1) 99 | assert.Equal(t, err1, nil) 100 | assert.Equal(t, resp1.Code, 0) 101 | } 102 | 103 | func TestTenant_DisbandChat(t *testing.T) { 104 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 105 | t.Log(e) 106 | t.Log(json.ToJsonIgnoreError(app)) 107 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 108 | t.Log(e) 109 | 110 | resp, err := tenant.DisbandChat(vo.UpdateChatData{ChatId: ""}) 111 | log.Info(json.ToJsonIgnoreError(resp), err) 112 | assert.Equal(t, err, nil) 113 | assert.Equal(t, resp.Code, 0) 114 | } 115 | 116 | func TestTenant_AddBot(t *testing.T) { 117 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 118 | t.Log(e) 119 | t.Log(json.ToJsonIgnoreError(app)) 120 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 121 | t.Log(e) 122 | 123 | resp, err := tenant.AddBot(vo.UpdateChatData{ChatId: "oc_95f13644b4182501420139ccee26c989"}) 124 | log.Info(json.ToJsonIgnoreError(resp), err) 125 | assert.Equal(t, err, nil) 126 | assert.Equal(t, resp.Code, 0) 127 | } 128 | -------------------------------------------------------------------------------- /sdk/robot_send_msg.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //发送消息卡片 https://open.feishu.cn/document/ukTMukTMukTM/uYTNwUjL2UDM14iN1ATN 12 | func (t Tenant) SendMessage(msg vo.MsgVo) (*vo.MsgResp, error) { 13 | reqBody := json.ToJsonIgnoreError(msg) 14 | respBody, err := http.Post(consts.ApiRobotSendMessage, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 15 | if err != nil { 16 | log.Error(err) 17 | return nil, err 18 | } 19 | respVo := &vo.MsgResp{} 20 | json.FromJsonIgnoreError(respBody, respVo) 21 | return respVo, nil 22 | } 23 | 24 | //发送消息卡片 https://open.feishu.cn/document/ukTMukTMukTM/uYTNwUjL2UDM14iN1ATN 25 | func (t Tenant) SendMessageBatch(msg vo.BatchMsgVo) (*vo.MsgResp, error) { 26 | reqBody := json.ToJsonIgnoreError(msg) 27 | respBody, err := http.Post(consts.ApiRobotSendBatchMessage, nil, reqBody, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 28 | if err != nil { 29 | log.Error(err) 30 | return nil, err 31 | } 32 | respVo := &vo.MsgResp{} 33 | json.FromJsonIgnoreError(respBody, respVo) 34 | return respVo, nil 35 | } 36 | -------------------------------------------------------------------------------- /sdk/robot_send_msg_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestTenant_SendMessage(t *testing.T) { 13 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 14 | t.Log(e) 15 | t.Log(json.ToJsonIgnoreError(app)) 16 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 17 | t.Log(e) 18 | 19 | resp, err := tenant.SendMessage(vo.MsgVo{ 20 | OpenId: "ou_433f2b1abf3e0ec316fd9e60d4cda654", 21 | MsgType: "text", 22 | Content: &vo.MsgContent{ 23 | Text: "test msg", 24 | }, 25 | }) 26 | log.Info(json.ToJsonIgnoreError(resp), err) 27 | assert.Equal(t, err, nil) 28 | assert.Equal(t, resp.Code, 0) 29 | 30 | resp, err = tenant.SendMessage(vo.MsgVo{ 31 | OpenId: "ou_433f2b1abf3e0ec316fd9e60d4cda654", 32 | MsgType: "post", 33 | Content: &vo.MsgContent{ 34 | Post: &vo.MsgPost{ 35 | ZhCn: &vo.MsgPostValue{ 36 | Title: "test title", 37 | Content: []interface{}{ 38 | []interface{}{ 39 | vo.MsgPostContentText{ 40 | Tag: "text", 41 | Text: "测试一哈", 42 | }, 43 | vo.MsgPostContentA{ 44 | Tag: "a", 45 | Text: "超链接", 46 | Href: "http://feishu.cn", 47 | }, 48 | }, 49 | []interface{}{ 50 | vo.MsgPostContentText{ 51 | Tag: "text", 52 | Text: "测试二行", 53 | }, 54 | vo.MsgPostContentA{ 55 | Tag: "a", 56 | Text: "超链接", 57 | Href: "http://feishu.cn", 58 | }, 59 | }, 60 | }, 61 | }, 62 | }, 63 | }, 64 | }) 65 | log.Info(json.ToJsonIgnoreError(resp), err) 66 | assert.Equal(t, err, nil) 67 | assert.Equal(t, resp.Code, 0) 68 | } 69 | 70 | //func TestNewFileUploadRequest(t *testing.T) { 71 | // data := make(map[string]string) 72 | // t.Log(NewFileUploadRequest("https://open.feishu.cn/open-apis/image/v4/put/", 73 | // data, "image", "C:\\Users\\admin\\Desktop\\11111.png")) 74 | //} 75 | 76 | func TestTenant_SendMessage_Card(t *testing.T) { 77 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 78 | t.Log(e) 79 | t.Log(json.ToJsonIgnoreError(app)) 80 | //tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 81 | tenant, e := BuildTenant(app.AppAccessToken, "2eb972ddc1cf1652") //测试企业二 82 | t.Log(e) 83 | 84 | resp, err := tenant.SendMessage(vo.MsgVo{ 85 | //OpenId: "ou_0e40d4035ceb951467beb62bb1f3e5ba", //陈 86 | //OpenId: "ou_dcff2de6a28060eff9f0d9665d2d28be",//我 87 | OpenId: "ou_44fbc016f49fa848a6182e4ff3c48780", //我 测试企业二 88 | MsgType: "interactive", 89 | Card: &vo.Card{ 90 | Config: &vo.CardConfig{ 91 | WideScreenMode: true, 92 | }, 93 | Header: &vo.CardHeader{ 94 | Title: &vo.CardHeaderTitle{ 95 | Tag: "plain_text", 96 | Content: "测试任务", 97 | }, 98 | }, 99 | Elements: []interface{}{ 100 | vo.CardElementContentModule{ 101 | Tag: "div", 102 | Text: &vo.CardElementText{ 103 | Tag: "plain_text", 104 | Content: "任务标题", 105 | }, 106 | }, 107 | vo.CardElementContentModule{ 108 | Tag: "div", 109 | Text: &vo.CardElementText{ 110 | Tag: "plain_text", 111 | Content: "负责人:aabc", 112 | }, 113 | }, 114 | vo.CardElementNote{ 115 | Tag: "note", 116 | Elements: []interface{}{ 117 | vo.CardElementTextAlt{ 118 | Tag: "plain_text", 119 | Content: "开始时间:无 ", 120 | }, 121 | vo.CardElementTextAlt{ 122 | Tag: "plain_text", 123 | Content: "截止时间:1990年12月12日 ", 124 | }, 125 | vo.CardElementTextAlt{ 126 | Tag: "plain_text", 127 | Content: "原截止时间:1990年12月12日", 128 | }, 129 | }, 130 | }, 131 | vo.CardElementActionModule{ 132 | Tag: "action", 133 | Actions: []interface{}{ 134 | vo.ActionButton{ 135 | Tag: "button", 136 | Text: vo.CardElementText{ 137 | Tag: "plain_text", 138 | Content: "任务详情", 139 | }, 140 | Url: "https://polaris.feishu.cn/docs/doccnldXu2b7ei0pmHbViQXXSGg", 141 | Type: "default", 142 | }, 143 | }, 144 | }, 145 | }, 146 | }, 147 | }) 148 | log.Info(json.ToJsonIgnoreError(resp), err) 149 | assert.Equal(t, err, nil) 150 | assert.Equal(t, resp.Code, 0) 151 | } 152 | 153 | func TestTenant_SendMessage_Card_Daily_Report(t *testing.T) { 154 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 155 | t.Log(e) 156 | t.Log(json.ToJsonIgnoreError(app)) 157 | tenant, e := BuildTenant(app.AppAccessToken, "2e99b3ab0b0f1654") 158 | t.Log(e) 159 | 160 | resp, err := tenant.SendMessage(vo.MsgVo{ 161 | OpenId: "ou_ce397f53085cb712373f71874e8eae78", 162 | MsgType: "interactive", 163 | Card: &vo.Card{ 164 | Config: &vo.CardConfig{ 165 | WideScreenMode: true, 166 | }, 167 | Header: &vo.CardHeader{ 168 | Title: &vo.CardHeaderTitle{ 169 | Tag: "plain_text", 170 | Content: "任务日报", 171 | }, 172 | }, 173 | Elements: []interface{}{ 174 | vo.CardElementContentModule{ 175 | Tag: "div", 176 | Fields: []vo.CardElementField{ 177 | { 178 | Text: vo.CardElementText{ 179 | Tag: "lark_md", 180 | Content: "**今日完成:** 100", 181 | }, 182 | }, 183 | { 184 | Text: vo.CardElementText{ 185 | Tag: "lark_md", 186 | Content: "**剩余:** 15", 187 | }, 188 | }, 189 | }, 190 | }, 191 | vo.CardElementActionModule{ 192 | Tag: "action", 193 | Actions: []interface{}{ 194 | vo.ActionButton{ 195 | Tag: "button", 196 | Text: vo.CardElementText{ 197 | Tag: "plain_text", 198 | Content: "去处理", 199 | }, 200 | Type: "link", 201 | }, 202 | }, 203 | }, 204 | }, 205 | }, 206 | }) 207 | log.Info(json.ToJsonIgnoreError(resp), err) 208 | assert.Equal(t, err, nil) 209 | assert.Equal(t, resp.Code, 0) 210 | } 211 | 212 | func TestTenant_SendMessage_PC_Applet(t *testing.T) { 213 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 214 | t.Log(e) 215 | t.Log(json.ToJsonIgnoreError(app)) 216 | tenant, e := BuildTenant(app.AppAccessToken, "1279794b670f575f") 217 | t.Log(e) 218 | 219 | resp, err := tenant.SendMessageBatch(vo.BatchMsgVo{ 220 | OpenIds: []string{"ou_4736aa27afbeb880894f5f98e19ac46e"}, 221 | MsgType: "interactive", 222 | Card: &vo.Card{ 223 | Config: &vo.CardConfig{ 224 | WideScreenMode: true, 225 | }, 226 | Header: &vo.CardHeader{ 227 | Title: &vo.CardHeaderTitle{ 228 | Tag: "plain_text", 229 | Content: "Pc小程序预览页", 230 | }, 231 | }, 232 | Elements: []interface{}{ 233 | vo.CardElementActionModule{ 234 | Tag: "action", 235 | Actions: []interface{}{ 236 | vo.ActionButton{ 237 | Tag: "button", 238 | Text: vo.CardElementText{ 239 | Tag: "plain_text", 240 | Content: "点击预览", 241 | }, 242 | MultiUrl: &vo.CardElementUrl{ 243 | PcUrl: "https://applink.feishu.cn/client/mini_program/open?appId=cli_9d5e49aae9ae9101&mode=sidebar-semi&path=pages/PC/Home/index", 244 | //PcUrl: "https://applink.feishu.cn/client/mini_program/open?appId=cli_9d5e49aae9ae9101&mode=sidebar-semi", 245 | IosUrl: "https://baidu.com", 246 | AndroidUrl: "http://app.bjx.cloud/project", 247 | }, 248 | Type: "default", 249 | }, 250 | }, 251 | }, 252 | }, 253 | }, 254 | }) 255 | log.Info(json.ToJsonIgnoreError(resp), err) 256 | assert.Equal(t, err, nil) 257 | assert.Equal(t, resp.Code, 0) 258 | } 259 | 260 | func TestTenant_SendNewIssueCard(t *testing.T) { 261 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 262 | t.Log(e) 263 | t.Log(json.ToJsonIgnoreError(app)) 264 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 265 | t.Log(e) 266 | 267 | resp, err := tenant.SendMessage(vo.MsgVo{ 268 | OpenId: "ou_e1b43c426e884c586d52751853896688", 269 | MsgType: "interactive", 270 | Card: &vo.Card{ 271 | //Config: &vo.CardConfig{ 272 | // WideScreenMode: true, 273 | //}, 274 | Header: &vo.CardHeader{ 275 | Title: &vo.CardHeaderTitle{ 276 | Tag: "plain_text", 277 | Content: "⏰ 新任务", 278 | }, 279 | }, 280 | Elements: []interface{}{ 281 | vo.CardElementContentModule{ 282 | Tag: "div", 283 | Fields: []vo.CardElementField{ 284 | { 285 | Text: vo.CardElementText{ 286 | Tag: "lark_md", 287 | Content: "**所属项目**\n个人安排", 288 | }, 289 | }, 290 | { 291 | Text: vo.CardElementText{ 292 | Tag: "lark_md", 293 | Content: "**\n任务标题**增加一个按钮", 294 | Lines: 3, 295 | }, 296 | }, 297 | { 298 | Text: vo.CardElementText{ 299 | Tag: "lark_md", 300 | Content: "**\n负责人: ** " + "符莎321312" + "\t\t **优先级: **" + "普通", 301 | }, 302 | }, 303 | }, 304 | }, 305 | vo.CardElementContentModule{ 306 | Tag: "div", 307 | Fields: []vo.CardElementField{ 308 | { 309 | Text: vo.CardElementText{ 310 | Tag: "lark_md", 311 | Content: "**计划开始时间:**", 312 | }, 313 | }, 314 | }, 315 | }, 316 | vo.CardElementActionModule{ 317 | Tag: "action", 318 | Actions: []interface{}{ 319 | vo.ActionDatePicker{ 320 | Tag: "picker_datetime", 321 | Placeholder: &vo.CardElementText{ 322 | Tag: "plain_text", 323 | Content: "修改计划开始时间", 324 | }, 325 | Confirm: &vo.CardElementConfirm{ 326 | Title: &vo.CardHeaderTitle{ 327 | Tag: "plain_text", 328 | Content: "确认要修改这个任务的计划开始时间吗?", 329 | }, 330 | Text: &vo.CardElementText{ 331 | Tag: "plain_text", 332 | Content: "⏰ 新任务", 333 | }, 334 | }, 335 | }, 336 | }, 337 | }, 338 | vo.CardElementContentModule{ 339 | Tag: "div", 340 | Fields: []vo.CardElementField{ 341 | { 342 | Text: vo.CardElementText{ 343 | Tag: "lark_md", 344 | Content: "**计划截止时间:**", 345 | }, 346 | }, 347 | }, 348 | }, 349 | vo.CardElementActionModule{ 350 | Tag: "action", 351 | Actions: []interface{}{ 352 | vo.ActionDatePicker{ 353 | Tag: "picker_datetime", 354 | Placeholder: &vo.CardElementText{ 355 | Tag: "plain_text", 356 | Content: "修改截止时间", 357 | }, 358 | Confirm: &vo.CardElementConfirm{ 359 | Title: &vo.CardHeaderTitle{ 360 | Tag: "plain_text", 361 | Content: "确认要修改这个任务的截止时间吗?", 362 | }, 363 | Text: &vo.CardElementText{ 364 | Tag: "plain_text", 365 | Content: "确定修改", 366 | }, 367 | }, 368 | }, 369 | }, 370 | }, 371 | vo.CardElementActionModule{ 372 | Tag: "action", 373 | Actions: []interface{}{ 374 | vo.ActionButton{ 375 | Tag: "button", 376 | Text: vo.CardElementText{ 377 | Tag: "lark_md", 378 | Content: "查看任务详情", 379 | }, 380 | Type: "default", 381 | }, 382 | }, 383 | }, 384 | }, 385 | }, 386 | }) 387 | log.Info(json.ToJsonIgnoreError(resp), err) 388 | assert.Equal(t, err, nil) 389 | assert.Equal(t, resp.Code, 0) 390 | 391 | } 392 | 393 | func TestTenant_SendMessageBatch(t *testing.T) { 394 | param := vo.MsgVo{ 395 | OpenId: "ou_dcff2de6a28060eff9f0d9665d2d28be", 396 | MsgType: "interactive", 397 | Card: &vo.Card{ 398 | Elements: []interface{}{ 399 | vo.CardElementImageModule{ 400 | Tag: "img", 401 | Alt: vo.CardElementText{ 402 | Tag: "lark_md", 403 | Content: "11", 404 | //Lines: 0, 405 | //Href: nil, 406 | }, 407 | Title: &vo.CardElementText{ 408 | Tag: "lark_md", 409 | Content: "11", 410 | }, 411 | ImgKey: "img_5bb79fbc-12f6-4a67-a4f9-dee33e5bd3fg", 412 | }, 413 | }, 414 | }, 415 | } 416 | 417 | t.Log(json.ToJson(param)) 418 | } 419 | -------------------------------------------------------------------------------- /sdk/role.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //获取角色列表 https://open.feishu.cn/document/ukTMukTMukTM/uYzMwUjL2MDM14iNzATN?lang=zh-CN 12 | func (t Tenant) GetRoleList() (*vo.RoleListResp, error){ 13 | respBody, err := http.Get(consts.ApiRoleList, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 14 | if err != nil{ 15 | log.Error(err) 16 | return nil, err 17 | } 18 | respVo := &vo.RoleListResp{} 19 | json.FromJsonIgnoreError(respBody, respVo) 20 | return respVo, nil 21 | } 22 | 23 | //获取角色成员列表 https://open.feishu.cn/document/ukTMukTMukTM/uczMwUjL3MDM14yNzATN?lang=zh-CN 24 | func (t Tenant) GetRoleMemberList() (*vo.RoleMemberListResp, error){ 25 | respBody, err := http.Get(consts.ApiRoleMemberList, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 26 | if err != nil{ 27 | log.Error(err) 28 | return nil, err 29 | } 30 | respVo := &vo.RoleMemberListResp{} 31 | json.FromJsonIgnoreError(respBody, respVo) 32 | return respVo, nil 33 | } -------------------------------------------------------------------------------- /sdk/role_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 7 | "gotest.tools/assert" 8 | "testing" 9 | ) 10 | 11 | func TestTenant_GetRoleList(t *testing.T) { 12 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 13 | t.Log(e) 14 | t.Log(json.ToJsonIgnoreError(app)) 15 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 16 | t.Log(e) 17 | 18 | resp, err := tenant.GetRoleList() 19 | log.Info(json.ToJsonIgnoreError(resp), err) 20 | assert.Equal(t, err, nil) 21 | assert.Equal(t, resp.Code, 0) 22 | } 23 | 24 | func TestTenant_GetRoleMemberList(t *testing.T) { 25 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 26 | t.Log(e) 27 | t.Log(json.ToJsonIgnoreError(app)) 28 | tenant, e := BuildTenant(app.AppAccessToken, "2ed263bf32cf1651") 29 | t.Log(e) 30 | 31 | resp, err := tenant.GetRoleMemberList() 32 | log.Info(json.ToJsonIgnoreError(resp), err) 33 | assert.Equal(t, err, nil) 34 | assert.Equal(t, resp.Code, 0) 35 | } -------------------------------------------------------------------------------- /sdk/scopes.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 9 | ) 10 | 11 | //1.查询租户授权状态 https://bytedance.feishu.cn/docs/doccnHJx2UbLZh5kiWjNawICyNd#dCNL6V 12 | func (t Tenant) GetScopes() (*vo.GetScopesResp, error){ 13 | respBody, err := http.Get(consts.ApiGetScopes, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 14 | if err != nil{ 15 | log.Error(err) 16 | return nil, err 17 | } 18 | respVo := &vo.GetScopesResp{} 19 | json.FromJsonIgnoreError(respBody, respVo) 20 | return respVo, nil 21 | } 22 | 23 | // 申请授权 https://bytedance.feishu.cn/docs/doccnHJx2UbLZh5kiWjNawICyNd#kHHiAa 24 | func (t Tenant) ApplyScopes() (*vo.ApplyScopesResp, error){ 25 | respBody, err := http.Post(consts.ApiApplyScopes, nil, "", http.BuildTokenHeaderOptions(t.TenantAccessToken)) 26 | if err != nil{ 27 | log.Error(err) 28 | return nil, err 29 | } 30 | respVo := &vo.ApplyScopesResp{} 31 | json.FromJsonIgnoreError(respBody, respVo) 32 | return respVo, nil 33 | } 34 | -------------------------------------------------------------------------------- /sdk/scopes_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 7 | "testing" 8 | ) 9 | 10 | func TestTenant_GetScopes(t *testing.T) { 11 | tenant := GetTenant("2e99b3ab0b0f1654") 12 | resp, err := tenant.GetScopes() 13 | if err != nil { 14 | t.Error(err) 15 | return 16 | } 17 | log.Info(json.ToJsonIgnoreError(resp)) 18 | } 19 | 20 | func TestTenant_ApplyScopes(t *testing.T) { 21 | tenant := GetTenant("2c0c7abea54f9758") 22 | resp, err := tenant.ApplyScopes() 23 | if err != nil { 24 | t.Error(err) 25 | return 26 | } 27 | log.Info(json.ToJsonIgnoreError(resp)) 28 | } 29 | 30 | // 测试时,公用方法,获取 tenant。 31 | func GetTenant(outOrgId string) *Tenant { 32 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 33 | if e != nil { 34 | log.Error(e) 35 | return nil 36 | } 37 | tenant, e := BuildTenant(app.AppAccessToken, outOrgId) 38 | if e != nil { 39 | log.Error(e) 40 | return nil 41 | } 42 | return tenant 43 | } 44 | -------------------------------------------------------------------------------- /sdk/sdk.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type App struct { 9 | AppId string 10 | AppSecret string 11 | AppAccessToken string 12 | } 13 | 14 | type Tenant struct { 15 | TenantAccessToken string 16 | } 17 | 18 | type User struct { 19 | UserAccessToken string 20 | } 21 | 22 | func BuildInternalApp(appId, appSecret string) (*App, error){ 23 | resp, err := GetAppAccessTokenInternal(appId, appSecret) 24 | if err != nil{ 25 | return nil, err 26 | } 27 | if resp.Code != 0{ 28 | return nil, errors.New(fmt.Sprintf("req err, code: %d, msg: %s", resp.Code, resp.Msg)) 29 | } 30 | return &App{ 31 | AppId: appId, 32 | AppSecret: appSecret, 33 | AppAccessToken: appSecret, 34 | }, nil 35 | } 36 | 37 | func BuildApp(appId, appSecret, appTicket string) (*App, error){ 38 | resp, err := GetAppAccessToken(appId, appSecret, appTicket) 39 | if err != nil{ 40 | return nil, err 41 | } 42 | if resp.Code != 0{ 43 | return nil, errors.New(fmt.Sprintf("req err, code: %d, msg: %s", resp.Code, resp.Msg)) 44 | } 45 | return &App{ 46 | AppId: appId, 47 | AppSecret: appSecret, 48 | AppAccessToken: resp.AppAccessToken, 49 | }, nil 50 | } 51 | 52 | func BuildTenantInternal(appId, appSecret string) (*Tenant, error){ 53 | resp, err := GetTenantAccessTokenInternal(appId, appSecret) 54 | if err != nil{ 55 | return nil, err 56 | } 57 | if resp.Code != 0{ 58 | return nil, errors.New(fmt.Sprintf("req err, code: %d, msg: %s", resp.Code, resp.Msg)) 59 | } 60 | return &Tenant{ 61 | TenantAccessToken: resp.TenantAccessToken, 62 | }, nil 63 | } 64 | 65 | func BuildTenant(appAccessToken, tenantKey string) (*Tenant, error){ 66 | resp, err := GetTenantAccessToken(appAccessToken, tenantKey) 67 | if err != nil{ 68 | return nil, err 69 | } 70 | if resp.Code != 0{ 71 | return nil, errors.New(fmt.Sprintf("req err, code: %d, msg: %s", resp.Code, resp.Msg)) 72 | } 73 | return &Tenant{ 74 | TenantAccessToken: resp.TenantAccessToken, 75 | }, nil 76 | } -------------------------------------------------------------------------------- /sdk/user.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/model/vo" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 8 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 9 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 10 | "net/url" 11 | ) 12 | 13 | //搜索用户 https://bytedance.feishu.cn/docs/doccnizryz7NKuUmVfkRJWeZGVc 14 | func (u User) SearchUser(query string, pageSize int, pageToken string) (*vo.SearchUserResp, error) { 15 | queryParams := map[string]interface{}{} 16 | if query != "" { 17 | queryParams["query"] = query 18 | } 19 | if pageSize > 0 { 20 | queryParams["page_size"] = pageSize 21 | } 22 | if pageToken != "" { 23 | queryParams["page_token"] = pageToken 24 | } 25 | respBody, err := http.Get(consts.ApiSearchUser, queryParams, http.BuildTokenHeaderOptions(u.UserAccessToken)) 26 | if err != nil { 27 | log.Error(err) 28 | return nil, err 29 | } 30 | respVo := &vo.SearchUserResp{} 31 | json.FromJsonIgnoreError(respBody, respVo) 32 | return respVo, nil 33 | } 34 | 35 | //使用手机号或邮箱获取用户ID https://open.feishu.cn/document/ukTMukTMukTM/uUzMyUjL1MjM14SNzITN 36 | func (t Tenant) BatchGetId(emails []string, mobiles []string) (*vo.BatchGetIdResp, error) { 37 | query := "" 38 | for _, email := range emails { 39 | query += fmt.Sprintf("emails=%s&", email) 40 | } 41 | for _, mobile := range mobiles { 42 | query += fmt.Sprintf("mobiles=%s&", url.QueryEscape(mobile)) 43 | } 44 | fullUrl := consts.ApiBatchGetUserId 45 | if len(query) > 0 { 46 | fullUrl += fmt.Sprintf("?%s", query[0:len(query)-1]) 47 | } 48 | respBody, err := http.Get(fullUrl, nil, http.BuildTokenHeaderOptions(t.TenantAccessToken)) 49 | if err != nil { 50 | log.Error(err) 51 | return nil, err 52 | } 53 | respVo := &vo.BatchGetIdResp{} 54 | json.FromJsonIgnoreError(respBody, respVo) 55 | return respVo, nil 56 | } 57 | -------------------------------------------------------------------------------- /sdk/user_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/galaxy-book/feishu-sdk-golang/core/consts" 5 | "github.com/galaxy-book/feishu-sdk-golang/core/util/http" 6 | "github.com/galaxy-book/feishu-sdk-golang/core/util/json" 7 | "github.com/galaxy-book/feishu-sdk-golang/core/util/log" 8 | "gotest.tools/assert" 9 | "testing" 10 | ) 11 | 12 | func TestUser_SearchUser(t *testing.T) { 13 | 14 | user := User{ 15 | UserAccessToken: "u-ahufHbuFHqXVOM0HqlXCxd", 16 | } 17 | 18 | resp, err := user.SearchUser("abcde", 0, "") 19 | t.Log(err) 20 | t.Log(json.ToJsonIgnoreError(resp)) 21 | } 22 | 23 | func TestSearch(t *testing.T) { 24 | body, e := http.Get("https://open.feishu.cn/api/v3/app/cli_9d5e49aae9ae9101/developer/search?name=l", nil) 25 | t.Log(e) 26 | t.Log(body) 27 | } 28 | 29 | func TestTenant_BatchGetId(t *testing.T) { 30 | app, e := BuildApp(consts.TestAppId, consts.TestAppSecret, consts.TestTicket) 31 | t.Log(e) 32 | t.Log(json.ToJsonIgnoreError(app)) 33 | tenant, e := BuildTenant(app.AppAccessToken, "12243f37560ed740") 34 | t.Log(e) 35 | 36 | resp, err := tenant.BatchGetId([]string{"xxxxxx@qq.com"}, []string{"+86xxxxxxx", "xxxxx"}) 37 | log.Info(json.ToJsonIgnoreError(resp), err) 38 | assert.Equal(t, err, nil) 39 | assert.Equal(t, resp.Code, 0) 40 | } 41 | -------------------------------------------------------------------------------- /test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func main() { 9 | m1 := map[int]interface{}{} 10 | m2 := map[int]interface{}{} 11 | count := 10000 12 | for i := 0; i < count; i++ { 13 | m1[i] = i 14 | m2[count-i-1] = count - i - 1 15 | } 16 | fmt.Println(reflect.DeepEqual(m1, m2)) 17 | } 18 | --------------------------------------------------------------------------------