diff --git a/.env.example b/.env.example index 0745dc3..74032c6 100644 --- a/.env.example +++ b/.env.example @@ -2,45 +2,45 @@ PORT=3000 NODE_ENV=production # 管理后台密码(首次部署务必修改) -ADMIN_PASSWORD=change-me-to-strong-password +ADMIN_PASSWORD=:e7P6ZaxwU9Uv8R # ===== 飞书配置 ===== # 在飞书开放平台创建企业自建应用后获取 -FEISHU_APP_ID=cli_xxxxxxxxxxxx -FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +FEISHU_APP_ID=cli_aae9a27533e2dbed +FEISHU_APP_SECRET=iZhaalG01oqkVDZsmdmOFdMzGtfFGzsD # 事件订阅的 Verification Token 和 Encrypt Key -FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxx +FEISHU_VERIFICATION_TOKEN=jPBz4l4fdCS25H95fSlQWeanmMzohUBm FEISHU_ENCRYPT_KEY= # 办理人的 open_id(程序以此身份完成办理任务) # 获取方式:飞书开放平台 API调试台 → 搜索"获取用户信息" → 用手机号查自己的 open_id -FEISHU_HANDLER_USER_ID=ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +FEISHU_HANDLER_USER_ID=ou_195f9c5d3d382732af4a665d7675fbfe # 飞书审批定义code(飞书管理后台 → 审批管理 → 打开对应表单 → URL中的 approvalCode 参数) -FEISHU_APPROVAL_FILE_IN=你的文件传入审批code -FEISHU_APPROVAL_FILE_OUT=你的文件传出审批code -FEISHU_APPROVAL_GROUP_PERM=你的红区组权限审批code +FEISHU_APPROVAL_FILE_IN=CC9211DE-851A-4E22-9A2B-337134F20F0E +FEISHU_APPROVAL_FILE_OUT=60650A89-AEDE-43AD-BEC3-5BB569B6F12F +FEISHU_APPROVAL_GROUP_PERM=935A65D1-3173-48F2-A8A1-B61318E31CAF # 告警通知群 webhook(群机器人) -FEISHU_ALERT_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx +FEISHU_ALERT_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/0e9ca0de-54e2-4bff-8621-32ccdbfa5623 # ===== 群晖 NAS 配置 ===== -SYNOLOGY_HOST=https://你的NAS_IP:5001 -SYNOLOGY_USERNAME=你的NAS账号 -SYNOLOGY_PASSWORD=你的NAS密码 +SYNOLOGY_HOST=https://10.2.3.40:5001 +SYNOLOGY_USERNAME=qwq +SYNOLOGY_PASSWORD=:e7P6ZaxwU9Uv8R # 是否跳过TLS证书验证(自签证书时设为true) SYNOLOGY_SKIP_TLS=true # ===== JumpServer 配置 ===== -JUMPSERVER_HOST=http://你的JumpServer地址 -JUMPSERVER_KEY_ID=你的API-Key-ID -JUMPSERVER_KEY_SECRET=你的API-Key-Secret -# ic1 资产的 asset_id(在JumpServer控制台 → 资产管理 → 点击ic1 → URL中的UUID) -JUMPSERVER_ASSET_IC1=你的ic1资产UUID +JUMPSERVER_HOST=http://10.2.3.41 +JUMPSERVER_KEY_ID=912c725a-145b-4ee9-8eb8-4dc33913349b +JUMPSERVER_KEY_SECRET=O4XnRa6ustUV7A4sU6KWaH8yAs3TXtxzLcTa +# ic1 资产的 asset_id(在JumpServer控制台查看) +JUMPSERVER_ASSET_IC1=c37d2b6d-3c4f-4d24-8792-40f3db2be54e # 系统用户(用于连接ic1的账号) JUMPSERVER_SYSTEM_USER=root # ===== Nextcloud 配置 ===== -NEXTCLOUD_HOST=http://你的Nextcloud地址 -NEXTCLOUD_USERNAME=你的Nextcloud账号 -NEXTCLOUD_PASSWORD=你的Nextcloud密码 +NEXTCLOUD_HOST=http://10.2.3.137:28080 +NEXTCLOUD_USERNAME=qwq +NEXTCLOUD_PASSWORD=03451420 # 共享默认权限(1=读, 15=读写删, 17=读+分享) NEXTCLOUD_SHARE_PERMISSIONS=1 diff --git a/src/routes/feishu-event.js b/src/routes/feishu-event.js index 18924cb..f598422 100644 --- a/src/routes/feishu-event.js +++ b/src/routes/feishu-event.js @@ -42,23 +42,74 @@ router.post('/event', async (req, res) => { }); /** - * 处理飞书事件 + * 处理飞书事件(支持 approval_instance 和 approval_task 两种) */ async function processEvent(body) { - // 飞书事件订阅 v2 格式 const header = body.header || {}; const event = body.event || {}; const eventType = header.event_type; - // 只处理审批实例状态变更事件 - if (eventType !== 'approval_instance') return; + logger.info(`[飞书事件] 收到事件类型: ${eventType}, 数据: ${JSON.stringify(event).substring(0, 300)}`); - const instanceCode = event.instance_code; - const approvalCode = event.approval_code; - const status = event.status; // APPROVED / REJECTED / PENDING + let instanceCode, approvalCode; - // 只处理进行中的审批(办理任务到达时 instance 仍为 PENDING) - if (status !== 'PENDING' && status !== 'APPROVED') return; + if (eventType === 'approval_task') { + // 审批任务事件:上级审批通过后,办理任务被创建时触发 + // 数据结构可能是 event.object 或 event 本身 + const taskObj = event.object || event; + instanceCode = taskObj.instance_code || taskObj.instance_id; + approvalCode = taskObj.approval_code; + const taskStatus = taskObj.status; + const taskUserId = taskObj.user_id; + + logger.info(`[飞书事件][task] instance=${instanceCode}, approval=${approvalCode}, status=${taskStatus}, user=${taskUserId}`); + + // 只处理 PENDING 状态的任务(新到达的待办) + if (taskStatus !== 'PENDING') { + logger.info(`[飞书事件][task] 非PENDING状态,跳过: ${taskStatus}`); + return; + } + + // 检查是否分配给配置的办理人 + const handlerId = config.feishu.handlerUserId; + if (handlerId && taskUserId && taskUserId !== handlerId) { + logger.info(`[飞书事件][task] 任务非本办理人,跳过: task_user=${taskUserId}, handler=${handlerId}`); + return; + } + + } else if (eventType === 'approval_instance') { + // 审批实例事件:整个审批单状态变化 + instanceCode = event.instance_code; + approvalCode = event.approval_code; + const status = event.status; + + if (status !== 'PENDING' && status !== 'APPROVED') return; + + // 检查当前 PENDING 任务是否分配给办理人 + const handlerId = config.feishu.handlerUserId; + if (handlerId) { + try { + const instance = await feishu.getApprovalInstance(approvalCode, instanceCode); + const taskList = instance.task_list || []; + const pendingTask = taskList.find(t => t.status === 'PENDING'); + if (!pendingTask || pendingTask.user_id !== handlerId) { + logger.info(`[飞书事件][instance] 当前待办非本程序办理任务,跳过`); + return; + } + } catch (e) { + logger.warn(`[飞书事件][instance] 获取实例详情失败: ${e.message}`); + } + } + + } else { + // 非目标事件 + return; + } + + if (!instanceCode || !approvalCode) { + logger.warn(`[飞书事件] 缺少 instanceCode 或 approvalCode,跳过`); + return; + } // 防重复处理 if (isApprovalProcessed(instanceCode)) { @@ -66,23 +117,7 @@ async function processEvent(body) { return; } - // 检查当前 PENDING 任务是否分配给办理人(过滤掉上级审批阶段) - const handlerId = config.feishu.handlerUserId; - if (handlerId) { - try { - const instance = await feishu.getApprovalInstance(approvalCode, instanceCode); - const taskList = instance.task_list || []; - const pendingTask = taskList.find(t => t.status === 'PENDING'); - if (!pendingTask || pendingTask.user_id !== handlerId) { - logger.info(`[飞书事件] 当前待办非本程序办理任务,跳过: instance=${instanceCode}`); - return; - } - } catch (e) { - logger.warn(`[飞书事件] 获取实例详情失败,继续处理: ${e.message}`); - } - } - - logger.info(`[飞书事件] 办理任务到达: code=${approvalCode}, instance=${instanceCode}`); + logger.info(`[飞书事件] 办理任务确认: code=${approvalCode}, instance=${instanceCode}`); // 根据审批定义 code 路由到不同工作流 if (approvalCode === config.feishu.approvalCodes.fileIn) { diff --git a/src/services/feishu-ws.js b/src/services/feishu-ws.js index 78ff7d0..91402a3 100644 --- a/src/services/feishu-ws.js +++ b/src/services/feishu-ws.js @@ -16,13 +16,14 @@ function startWsClient() { const wsClient = new lark.WSClient({ appId: config.feishu.appId, appSecret: config.feishu.appSecret, - loggerLevel: lark.LoggerLevel.info, + loggerLevel: lark.LoggerLevel.debug, // 调试阶段用 debug,稳定后改回 info }); wsClient.start({ eventDispatcher: new lark.EventDispatcher({}).register({ + // 审批实例状态变更(整个审批单的状态变化) 'approval_instance': async (data) => { - logger.info('[飞书WS] 收到审批事件:', JSON.stringify(data).substring(0, 200)); + logger.info('[飞书WS][approval_instance] 收到事件:', JSON.stringify(data).substring(0, 500)); if (eventHandler) { try { await eventHandler({ @@ -30,14 +31,29 @@ function startWsClient() { event: data, }); } catch (err) { - logger.error(`[飞书WS] 事件处理失败: ${err.message}`); + logger.error(`[飞书WS] approval_instance 处理失败: ${err.message}`); + } + } + }, + + // 审批任务状态变更(办理任务到达时会触发这个) + 'approval_task': async (data) => { + logger.info('[飞书WS][approval_task] 收到事件:', JSON.stringify(data).substring(0, 500)); + if (eventHandler) { + try { + await eventHandler({ + header: { event_type: 'approval_task' }, + event: data, + }); + } catch (err) { + logger.error(`[飞书WS] approval_task 处理失败: ${err.message}`); } } }, }), }); - logger.info('[飞书WS] 长连接已启动,等待审批事件...'); + logger.info('[飞书WS] 长连接已启动,监听 approval_instance + approval_task 事件'); } module.exports = { startWsClient, setEventHandler };