MCP-FEISHU

by NINGyv179
Verified
/** * Service for interacting with Eolink OpenAPI */ import axios from "axios"; // import { Api, Project, ApiTestRequest, ApiTestResponse } from '../models/api.js'; class FeishuService { private APP_ID: string; private APP_SECRET: string; private TABLE_ID: string; private APP_TOKEN: string; constructor() { // # 缺陷清单所绑定的应用 ID this.APP_ID = process.env.APP_ID || ""; // # 缺陷清单所绑定的应用 Secret this.APP_SECRET = process.env.APP_SECRET || ""; // # 缺陷清单所绑定的表格 ID,可以在表格页面 URL 中找到,(目前绑定的是缺陷清单这个表,如需绑定其他表格,需要道对应表中绑定应用,然后使用对应的应用信息和表格id去操作) this.TABLE_ID = process.env.TABLE_ID || ""; // # 这个token是固定的,每个人登录都有一个,表格页面F12搜索请求,meta/?token=,即可看到,可替换成个人的 this.APP_TOKEN = process.env.APP_TOKEN || ""; } /** * 获取租户访问令牌(鉴权) * @returns */ async getTenantAccessToken() { const { data } = await axios.post( "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal", { app_id: this.APP_ID, app_secret: this.APP_SECRET, }, { headers: { "Content-Type": "application/json", }, } ); const tokenMap = data; const TENANT_ACCESS_TOKEN = tokenMap?.tenant_access_token || ""; return TENANT_ACCESS_TOKEN; } /** * 根据字段值获取记录链接 * @param fieldName 字段名 * @param fieldValue 字段值 * @returns 记录链接 */ async getRecord(fieldName: string, fieldValue: string): Promise<string | null> { const tenantAccessToken = await this.getTenantAccessToken(); // 调用列出记录 API const { data } = await axios.get( `https://open.feishu.cn/open-apis/bitable/v1/apps/${this.APP_TOKEN}/tables/${this.TABLE_ID}/records`, { headers: { Authorization: `Bearer ${tenantAccessToken}`, }, params: { filter: `CurrentValue.[${fieldName}] = "${fieldValue}"`, // 修改过滤条件的格式 }, } ); if (data.code !== 0) { throw new Error(`Failed to get records: ${data.msg}`); } // 获取记录 ID const records = data.data.items; if (records.length === 0) { return null; // 没有找到匹配的记录 } const recordId = records[0].record_id; // 构造记录链接 let sharedLink = '查找不到此记录' if (recordId) { sharedLink = (await this.getSharedLink(recordId)) || sharedLink; } return sharedLink; } /** * 获取分享链接 * @param recordId 记录ID * @returns */ async getSharedLink(recordId: string): Promise<string | null> { const tenantAccessToken = await this.getTenantAccessToken(); // 调用批量获取记录 API const { data } = await axios.post( `https://open.feishu.cn/open-apis/bitable/v1/apps/${this.APP_TOKEN}/tables/${this.TABLE_ID}/records/batch_get`, { record_ids: [recordId], with_shared_url: true, // 请求返回分享链接 }, { headers: { Authorization: `Bearer ${tenantAccessToken}`, "Content-Type": "application/json", }, } ); if (data.code !== 0) { throw new Error(`Failed to get shared link: ${data.msg}`); } const records = data.data.records; if (records.length === 0 || !records[0].shared_url) { return null; // 没有找到分享链接 } return records[0].shared_url; // 返回分享链接 } /** * 查找特定时间段内修改了状态的记录 * @param startDate 开始日期(时间戳,毫秒) * @param endDate 结束日期(时间戳,毫秒) * @returns {Promise<any[]>} 返回记录列表 */ async getRecordsByStatus(status: string, startDate: number, endDate: number): Promise<any[]> { const tenantAccessToken = await this.getTenantAccessToken(); // 构造请求体 const requestBody = { filter: { conjunction: "and", conditions: [ { field_name: "状态", operator: "is", value: [status], // 筛选状态字段为指定值(如“已修复”) }, { field_name: "最近更新时间", operator: "isGreaterEqual", value: [startDate], // 开始日期 }, { field_name: "最近更新时间", operator: "isLessEqual", value: [endDate], // 结束日期 }, ], }, sort: [ { field_name: "最近更新时间", desc: true, // 按最近更新时间倒序排序 }, ], field_names: ["状态", "最近更新时间"], // 返回字段 }; // 调用查询记录 API const { data } = await axios.post( `https://open.feishu.cn/open-apis/bitable/v1/apps/${this.APP_TOKEN}/tables/${this.TABLE_ID}/records/search`, requestBody, { headers: { Authorization: `Bearer ${tenantAccessToken}`, "Content-Type": "application/json", }, } ); if (data.code !== 0) { throw new Error(`Failed to get records: ${data.msg}`); } return data.data.items; // 返回记录列表 } } export default new FeishuService();