NODE 04 处理飞书用户任务列表

node操作飞书,通过用户任务列表获取该用户任务id后在第三方审批实例中删除通知任务

绘图软件参考在线网址 https://plus.excalidraw.com/
目录

前言

一、思路

二、实现步骤

1.代码执行

2.补充说明

总结展望



前言

飞书与多个应用系统集成后,相关应用(OA系统,档案系统等)的审批流程提交到审批人同步飞书消息。现由于上线新系统新建部分测试用审批流程,需要删除部分人员每天收到的部分审批提醒消息。


查询(查询用户的任务列表 /open-apis/approval/v4/tasks/query) 删除(同步三方审批实例

一、思路

通过应用 查询用户的任务列表 获取到该用户的相关任务信息,如下:

"process_code": "9F610589-B541-4F00-BE22-915D8A436E9D",

"process_external_id": "40B1BCD0BF084A41A2A681096A5955AD",

在 同步第三方审批实例 应用中,输入对应的参数删除相应任务。参数如下:

"approval_code": "81D31358-93AF-92D6-7425-01A5D67C4E71",

"status": "PENDING",

"instance_id": "24492654",

"links": {

"pc_link": "https://applink.feishu.cn/client/mini_program/open?mode=appCenter\&appId=cli_9c90fc38e07a9101\&path=pc/pages/detail?id=1234"

},

相关应用参数与关联对应关系如图所示:

二、实现步骤

1.代码执行

在写代码前需要导入飞书官方库@larksuiteoapi/node-sdk,在JS文件路径cmd输入命令:

npm install @larksuiteoapi/node-sdk

删除相关任务代码如下:

javascript 复制代码
// node-sdk使用说明:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/server-side-sdk/nodejs-sdk/preparation-before-development

const lark = require('@larksuiteoapi/node-sdk');
const taskMap = new Map();
const processExternalId = "processid 需要自行替换";
let  tenantToken = "t-g1042c9SHFKY5CBVHUAIGAO5QNKM2P4FCZKJXCNV";
const userId =   'your userid';
 const client = new lark.Client({
	appId: '自行替换相关appid',
	appSecret: '自行替换 app_secret',
	// disableTokenCache为false时,SDK会自动管理租户token的获取与刷新,无需使用lark.withTenantToken("token")手动传递token
	disableTokenCache: true
});

// 定义前置任务(获取令牌)
async function preTask() {
  try {
    const res = await client.auth.v3.tenantAccessToken.internal({
      data: { app_id: 'your appid', app_secret: 'your app secret' }
    }, lark.withTenantToken(""));
    console.log('0021  tenant token resp:', res && res.data ? res.data : res);
	console.log('tenant access token:',  res.tenant_access_token);
    if (res && res.tenant_access_token) {
      tenantToken = res.tenant_access_token;
    } else {
      throw new Error('no tenant_access_token in response');
    }
    console.log('tenantToken refreshed:', tenantToken);
    return tenantToken;
  } catch (e) {
    console.error('preTask error:', e && e.response && e.response.data ? e.response.data : e);
    throw e;
  }
}

// 定义后置任务
async function approveThirdTask(tasks) {
  if (tasks.length === 0) {
    console.log('no tasks to post');
    return;
  }

   
    try {
      // 只处理你关心的字段(可先校验)
      const approval_code = tasks.get("approval_code");               // -> approval_code
      const instance_id = tasks.get("instance_id");            // -> instance_id
      const pc_link = tasks.get("pc_link");                   // -> links.pc_link

      if (!approval_code || !instance_id) {
        console.warn('skianskip task, missing approval_code or instance_id', tasks);
        console.warn('approval_code:', approval_code, 'instance_id:', instance_id);
        return;
      }

      const payload = {
        data: {
          approval_code: approval_code,
          status: 'APPROVED',            // 根据需要设置
          instance_id: instance_id,
          links: {
            pc_link: pc_link
          },
          // 可选时间/locale 等,按需要赋值或删掉
          start_time: Date.now().toString(),
          end_time: Date.now().toString(),
          update_time: Date.now().toString(),
          i18n_resources: [{
            locale: 'zh-CN',
            texts: [{ key: '', value: '' }],
            is_default: 'true'
          }]
        }
      };

      const resp = await client.approval.v4.externalInstance.create(
        payload,
        lark.withTenantToken(tenantToken)
      );

      console.log('externalInstance.create result for', instance_id, resp);

    } catch (e) {
      console.error('postTask error for task', tasks.get("process_external_id"), e && e.response ? e.response.data : e);
    }
 

  console.log('postTask done');
}

(async () =>{

	await preTask(); // 前置任务获取令牌并打印
console.log('using tenantToken:', tenantToken);


 const res = await  client.approval.v4.task.query({
		params: {
			page_size: 100,
			user_id: userId,
			topic: '1',
			user_id_type: 'user_id',
		},
	},
 lark.withTenantToken(tenantToken)
).then(res => {
  const tasks = res && res.data && res.data.tasks ? res.data.tasks : [];
  if (tasks.length === 0) {
    console.log('no tasks');
    return;
  }
  tasks.forEach((t, idx) => {
     const key =t.process_external_id;
	taskMap.set(key, JSON.stringify(t));
  	console.log(`task[${idx}] definition_code:`, t.definition_code);
    console.log(`task[${idx}] process_external_id:`, t.process_external_id);
    console.log(`task[${idx}] pc:`, (t.urls && t.urls.pc) || null);
//  以 process_external_id 作为 key,值为 其他 task 信息,作为对象存储 到 内存 中
if(t.process_external_id === processExternalId){
  console.log('Found target task:', t);
  taskMap.set("approval_code",t.definition_code );
  taskMap.set("instance_id",t.process_external_id );
  taskMap.set("pc_link", t.urls?.pc || null);

  console.log('125  Extracted approval_code:', t.definition_code);
  console.log('126  Extracted instance_id:', t.process_external_id);
  console.log('127  Extracted pc_link:', t.urls?.pc || null);  
}

  });
}).then(
	() => {
		console.log('taskMap:', taskMap);
		console.log('taskMap size:', taskMap.size);
		// console.log('taskMap values:', JSON.stringify(Array.from(taskMap.values()), null, 4));
	},
).catch(e => {
	console.error(e);
});

await approveThirdTask(taskMap); // 后置任务

})();
 

以上代码写完后保存好不能直接执行,需要先在DemoDS2602.js文件路径cmd下输入命令

node DemoDS2602.js

执行以上命令后在进行删除相关任务。在以上代码中,只需要输入用户id(userId)与流程的ID(processExternalId)即可运行。

2.补充说明

以上代码实现了三个功能并同步顺序执行,即 获取访问令牌,使用 查询用户任务信息 应用 根据userid与任务id(processExternalId)获取要删除的任务信息,根据以上要删除的任务信息调用 同步第三方审批实例 应用删除相应任务。

获取令牌方法如下:

javascript 复制代码
async function preTask() {
  try {
    const res = await client.auth.v3.tenantAccessToken.internal({
      data: { app_id: 'your APPid', app_secret: 'your app_secret' }
    }, lark.withTenantToken(""));
    console.log('0021  tenant token resp:', res && res.data ? res.data : res);
	console.log('tenant access token:',  res.tenant_access_token);
    if (res && res.tenant_access_token) {
      tenantToken = res.tenant_access_token;
    } else {
      throw new Error('no tenant_access_token in response');
    }
    console.log('tenantToken refreshed:', tenantToken);
    return tenantToken;
  } catch (e) {
    console.error('preTask error:', e && e.response && e.response.data ? e.response.data : e);
    throw e;
  }
}

其中app_secret与app_id均为应用的配置信息,需要替换申请的应用对应配置。

删除任务代码如下:

javascript 复制代码
// 删除相应任务
async function approveThirdTask(tasks) {
  if (tasks.length === 0) {
    console.log('no tasks to post');
    return;
  }

   
    try {
      // 只处理你关心的字段(可先校验)
      const approval_code = tasks.get("approval_code");               // -> approval_code
      const instance_id = tasks.get("instance_id");            // -> instance_id
      const pc_link = tasks.get("pc_link");                   // -> links.pc_link

      if (!approval_code || !instance_id) {
        console.warn('skianskip task, missing approval_code or instance_id', tasks);
        console.warn('approval_code:', approval_code, 'instance_id:', instance_id);
        return;
      }

      const payload = {
        data: {
          approval_code: approval_code,
          status: 'APPROVED',            // 根据需要设置
          instance_id: instance_id,
          links: {
            pc_link: pc_link
          },
          // 可选时间/locale 等,按需要赋值或删掉
          start_time: Date.now().toString(),
          end_time: Date.now().toString(),
          update_time: Date.now().toString(),
          i18n_resources: [{
            locale: 'zh-CN',
            texts: [{ key: '', value: '' }],
            is_default: 'true'
          }]
        }
      };

      const resp = await client.approval.v4.externalInstance.create(
        payload,
        lark.withTenantToken(tenantToken)
      );

      console.log('externalInstance.create result for', instance_id, resp);

    } catch (e) {
      console.error('postTask error for task', tasks.get("process_external_id"), e && e.response ? e.response.data : e);
    }
 

  console.log('postTask done');
}

主方法执行,将token获取到后查询相关任务,封装要删除的任务信息(process_external_id,urls.pc等)到指定map(taskMap)中提供给删除方法(await approveThirdTask(taskMap) ),代码如下:

javascript 复制代码
 const res = await  client.approval.v4.task.query({
		params: {
			page_size: 100,
			user_id: userId,
			topic: '1',
			user_id_type: 'user_id',
		},
	},
 lark.withTenantToken(tenantToken)
).then(res => {
  const tasks = res && res.data && res.data.tasks ? res.data.tasks : [];
  if (tasks.length === 0) {
    console.log('no tasks');
    return;
  }
  tasks.forEach((t, idx) => {
     const key =t.process_external_id;
	taskMap.set(key, JSON.stringify(t));
  	console.log(`task[${idx}] definition_code:`, t.definition_code);
    console.log(`task[${idx}] process_external_id:`, t.process_external_id);
    console.log(`task[${idx}] pc:`, (t.urls && t.urls.pc) || null);
//  以 process_external_id 作为 key,值为 其他 task 信息,作为对象存储 到 内存 中
if(t.process_external_id === processExternalId){
  console.log('Found target task:', t);
  taskMap.set("approval_code",t.definition_code );
  taskMap.set("instance_id",t.process_external_id );
  taskMap.set("pc_link", t.urls?.pc || null);

  console.log('125  Extracted approval_code:', t.definition_code);
  console.log('126  Extracted instance_id:', t.process_external_id);
  console.log('127  Extracted pc_link:', t.urls?.pc || null);  
}

  });
}).then(
	() => {
		console.log('taskMap:', taskMap);
		console.log('taskMap size:', taskMap.size);
		// console.log('taskMap values:', JSON.stringify(Array.from(taskMap.values()), null, 4));
	},
).catch(e => {
	console.error(e);
});

相关同步方式:(async/await 是异步非阻塞,then)以上两个方法(preTask(),approveThirdTask(taskMap))定义使用了async(引用 都使用了await修饰),主方法执行前也使用了(async () =>{ 进行修饰,即该主方法也使用同步方式;内部代码执行流程保证同步使用了then方式执行

代码中 preTask()approveThirdTask(taskMap) 均定义为 async 函数,主执行逻辑使用立即执行异步函数表达式(IIFE)(async () => { ... })() 包裹,内部通过 await 确保串行执行:

  1. 先执行 await preTask() 获取并刷新租户令牌

  2. 再通过 await client.approval.v4.task.query() 查询任务列表,链式调用 .then() 处理响应数据并填充 taskMap

  3. 最后执行 await approveThirdTask(taskMap) 提交审批

其中任务查询部分采用 async/await.then() 混用方式:顶层使用 await 等待 Promise 完成,内部使用 .then() 进行响应数据的链式处理与错误捕获。


整体应用关联关系与代码执行流程如图:


总结展望

该任务代码大量参考了AI,后期优化方向估计也会借助AI进行。当前由于客观因素,一次只能删除一个飞书审批任务,人员ID也需要手动到后台查询,后期可以考虑引入MCP与飞书机器人,补充获取人员id的方法,增加删除人员所有任务的方法,通过对话的方式执行,是否会带来相应风险也需要谨慎评估

相关推荐
weixin_446260857 小时前
win11本地部署openclaw实操第4集-wsl方式实现飞书机器人功能
java·机器人·飞书
是孑然呀8 小时前
【笔记】影刀RPA+飞书多维表格
笔记·飞书·rpa
偕臧x9 小时前
OpenClaw + 飞书(Feishu)环境搭建指南
部署·飞书·openclaw·feishu·spawn einval
qq_314009831 天前
Windows10+WSL部署Openclaw接入飞书实践
人工智能·飞书
技术程序猿华锋1 天前
OpenClaw (CloudBot) 国内完美运行指南:自定义API 代理与飞书协同部署
人工智能·飞书·openclaw
Neolnfra2 天前
本地部署中文OpenClaw 飞书机器人部署指南
飞书·openclaw
涛涛讲AI3 天前
飞书多维表格中拼接,分隔,截取函的使用教程
飞书·多维表格
Peter·Pan爱编程4 天前
打造私有AI助理:OpenClaw + Ollama本地大模型 + 飞书机器人全接入指南
人工智能·机器人·飞书
能源革命4 天前
OpenClaw配置Bot接入飞书机器人+Kimi2.5
飞书