Skip to content

飞书

织信支持与飞书进行集成,通过飞书自建应用并完成相关配置,即可在飞书工作台使用织信。

1.注册飞书

首先,需要在 飞书 注册账号创建团队。

2.在飞书开放平台创建企业自建应用

创建完团队后,需要进入 飞书开放平台 创建一个企业自建应用。 然后按照要求填写好应用资料,填写完毕点击「确定创建」 点击刚创建的应用,在「凭证与基础信息」中,将参数 AppID 和 App Secret 记录下来。

3.自建应用设置权限

选择「权限管理」, 开通以下权限:

  • 获取用户 user ID
  • 获取用户基本信息
  • 获取部门基础信息
  • 获取部门组织架构信息

在「权限管理」中,将需要设置的权限一一选择,然后点击「批量开通」。如下图所示:

4.获取织信团队ID

打开织信工作台,选择「团队 >> 团队设置」,找到「团队ID」。

5.自建应用安全设置

在「安全设置」中的「重定向 URL」中添加:https://next.informat.cn/web0/feishu/login/{团队ID} URL最后一个参数是上一步获取的团队ID

6.自建应用启用网页

选择「应用功能 >> 网页」,启用网页,设置桌面端和移动端主页后,点击「保存」。

如需跳转系统工作台应用列表,则设置:

  • 桌面端主页:https://next.informat.cn/web0/feishu/enter/{团队ID}
  • 移动端主页:https://next.informat.cn/web0/feishu/mobile_enter/{团队ID}

如需跳转指定应用首页,则设置:

  • 桌面端主页:https://next.informat.cn/web0/feishu/enter/{团队ID}/{应用ID}
  • 移动端主页:https://next.informat.cn/web0/feishu/mobile_enter/{团队ID}/{应用ID}

链接最后一个参数是上一步得到的团队ID:xqrqdfvte9tnk

7.织信第三方集成飞书

在织信工作台「团队 >> 团队设置」下,找到「第三方集成」,选择「飞书」,点击「启用此功能」,填入第2步保存的飞书AppID 和 App Secret后点击「保存」。

8.织信团队成员设置飞书账号ID

  • 8.1 在织信工作台「团队 >> 团队设置」下,找到「成员」,点击「编辑」按钮,打开成员编辑页面。
  • 8.2 打开飞书管理后台,在「组织架构 >> 成员与部门」下找到织信成员对应的飞书成员,点击对应成员「姓名」打开成员详情,找到用户ID,并复制。
  • 8.3 在8.1打开的编辑成员页面里,「飞书账号ID」,填入8.2得到的飞书用户ID,并保存

9.飞书自建应用发布版本

在飞书开放平台,打开上面建好的自建应用页面。找到「应用发布>> 版本管理与发布」,点击「创建版本」 版本详情页面如下填写: 应用版本号: 1.0.0 更新说明: 初始版本 点击「保存」 然后点击「申请线上发布」。

10.飞书工作台打开织信

在飞书客户端或者移动端,在工作台的列表中,找到上述创建好的自建应用,点击打开,就会免登录跳转到织信的工作台。 如果跳转到织信登录页面,说明当前飞书账号未和织信账号绑定,需要按照第8步流程,给织信团队成员设置飞书账号ID

11.飞书通知

在飞书开放平台,打开自建应用页面。找到「应用功能>> 机器人」,点击「启用机器人」

在「权限管理」中,将以下需要设置的权限一一选择,然后点击「批量开通」。如下图所示:

  • im:message
  • im:message:send_as_bot

以上设置完成后,发送通时,会将消息同步发送给已绑定的飞书用户

12.飞书扫码登录

  • 12.1 在织信工作台「团队 >> 团队设置 >> 登录页面」下,点击「启用团队登录页面」、「启用第三方扫码登录」。
  • 12.2 在织信工作台「团队 >> 团队设置 >> 第三方集成」下找到「飞书」,点击「启用登录」,启用飞书扫码登录。

需要注意,扫码登录需要给团队成员绑定飞书账号ID

13.对接飞书同步组织架构和成员

13.1 概述

在企业内部通常会使用飞书等即时通讯工具作为日常办公的沟通工具,这类工具通常提供了组织架构管理、用户管理、通知、待办等功能。 下面是对接飞书并同步组织架构和成员到织信的步骤。

13.2 实现步骤

  • 同步飞书组织架构前,需要先完成「第三方集成 >> 飞书」的集成。
  • 开通通讯录权限,至少包含获取部门基础信息、获取通讯录部门组织架构信息、获取用户基本信息、获取用户userID、获取用户手机号、以应用身份读取通讯录这六个权限。

13.3 实现代码

javascript
const defaultRole='normal';//授权给新用户的团队角色
const defaultPassword='123456';//新用户设置的密码,用户名为手机号
const syncMembers=true;//是否同步成员

export function syncDepartments() {
	syncDepartmentList('0', 10, null);
	console.log('syncDepartments finished');
}

function syncDepartmentList(parentId, pageSize, pageToken) {
	 var url=`https://open.feishu.cn/open-apis/contact/v3/departments/${parentId}/children?department_id_type=department_id&fetch_child=false&page_size=${pageSize}&user_id_type=user_id`;
	if(pageToken){
		url=url+'&page_token='+pageToken;
	}
		const resp = httpGet(url);
		const result = JSON.parse(resp.body());
	console.log('body',resp.body());
	if(result.data.items){
		result.data.items.forEach(item=>{
			var dept=informat.dept.getDept(item.department_id);
			if (dept==null) {
				console.log('新增部门',item);
				console.log('parentId',parentId)
				informat.dept.addDept({
									'id': item.department_id,
									'name': item.name,
									'remark': item.name,
									'parentId': item.parent_department_id === '0' ? null : parentId
							});
			}else{
				console.log('编辑部门',item);
				informat.dept.updateDept({
								'id': item.department_id,
									'name': item.name
							});
			}
			syncDepartmentList(item.department_id, pageSize, null);
		});
	}
	var hasMore=result.data.has_more;
	if(hasMore){
		syncDepartmentList(parentId, pageSize, result.data.page_token);
	}
	//同步成员
	if(syncMembers){
		syncMemberList(parentId, pageSize, null)
	}
}
//
function syncMemberList(departmentId, pageSize, pageToken) {
	var url=`https://open.feishu.cn/open-apis/contact/v3/users/find_by_department?department_id_type=department_id&department_id=${departmentId}&page_size=${pageSize}&user_id_type=user_id`;
	if(pageToken){
		url=url+'&page_token='+pageToken;
	}
		const resp = httpGet(url);
	const result = JSON.parse(resp.body());
	console.log('body',resp.body());
	if(result.data.items){
		result.data.items.forEach(item=>{
			var memberList=informat.company.queryCompanyMemberList({
					pageIndex:1,
					pageSize:1,
					filter:{
							conditionList:[{
									"fieldId":"feishuUserId",
									"opt":"eq",
						"value":item.user_id
							}]
					}
			});
			var departmentList=item.department_ids;
			if(memberList.length==0){//新增
				var accountId=addAccount(item);
				var roleList=defaultRole==null?[]:[defaultRole];
				console.log('新增团队成员',item, departmentId);
				informat.company.addCompanyMember(accountId, departmentList, roleList);
				informat.company.updateCompanyMember({
						id: accountId,
						feishuUserId: item.user_id,
				});
			}else{//编辑
				var member=memberList[0];
				console.log('编辑团队成员', member.id);
				informat.company.updateCompanyMember({
						id: member.id,
						departmentList: departmentList,
				});
			}
		});
	}
	var hasMore=result.data.has_more;
	if(hasMore){
		syncMemberList(departmentId, pageSize, result.data.page_token);
	}
}

function addAccount(item){
	var mobile=item.mobile;
	if(mobile==null){
		throw new Error('员工手机号不能为空,请开放查看手机权限');
	}
	if(mobile.length==14){
		mobile=mobile.substr(3);
	}
	var accountList=informat.system.queryAccountList({
			pageIndex: 1,
			pageSize: 1,
			filter: {
					conditionList: [
							{
									fieldId: 'mobileNo',
									opt: 'eq',
									value: mobile
							}
					]
			}
	});
	var accountId=null;
	if(accountList.length==0){//账号不存在
		console.log('新增账号',item);
		accountId=informat.system.addAccount({
			oid: item.user_id,
				name: item.name,
				avatar: 'pic15.png',
				userName: mobile,
				mobileNo: mobile,
				email: item.email,
				password: defaultPassword
		});
	}else{
		var oldAccount=accountList[0];
		accountId=oldAccount.id;
		console.log('编辑账号',item);
		informat.system.updateAccount({
				id: oldAccount.id,
				name: item.name,
			userName: mobile,
				mobileNo: mobile
		}); 
	}
	return accountId;
}


function httpGet(url){
	var feishuAccessToken=informat.app.feishuAccessToken();
	var resp=informat.http.request({
				headers: {
						"Authorization": "Bearer " + feishuAccessToken
				},
				method: "GET",
				timeout: 30000,
				url: url
		});
	if (200 != resp.statusCode()) {
		console.log('url',url)
		console.log('resp statusCode',resp.statusCode());
		console.log('resp body',resp.body())
				throw new Error('发起http请求失败,请联系开发人员');
		}
	return resp;
}

13.4 常见错误