feat(#3): 飞书消息发送 - Webhook签名校验+项目通知/里程碑提醒/风险预警/决策请求

This commit is contained in:
2026-04-11 18:37:28 +08:00
parent 17297a231e
commit 107a972647

102
src/server/feishu.ts Normal file
View File

@@ -0,0 +1,102 @@
/**
* 飞书消息发送模块
* 通过飞书自定义机器人Webhook发送项目通知
*/
const FEISHU_WEBHOOK = 'https://open.feishu.cn/open-apis/bot/v2/hook/58321c74-5881-4f41-bcd4-85f4d7c5b3c1';
const FEISHU_SECRET = 'UgCdzrcci4s9YS1GSAHt4e';
import { createHmac } from 'crypto';
/**
* 生成飞书签名
*/
function generateSign(timestamp: number, secret: string): string {
const stringToSign = `${timestamp}\n${secret}`;
const hmac = createHmac('sha256', stringToSign);
return hmac.digest('base64');
}
export interface FeishuMessageOptions {
/** 消息内容 */
text: string;
/** 是否使用签名校验 */
sign?: boolean;
}
/**
* 发送飞书文本消息
*/
export async function sendFeishuMessage(options: FeishuMessageOptions): Promise<{ ok: boolean; code: number; msg: string }> {
const { text, sign = true } = options;
const timestamp = Math.floor(Date.now() / 1000);
const body: Record<string, unknown> = {
msg_type: 'text',
content: { text },
};
if (sign) {
body.timestamp = String(timestamp);
body.sign = generateSign(timestamp, FEISHU_SECRET);
}
try {
const response = await fetch(FEISHU_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const result = await response.json();
return {
ok: result.code === 0 || result.StatusCode === 0,
code: result.code ?? result.StatusCode ?? -1,
msg: result.msg ?? result.StatusMessage ?? '',
};
} catch (error) {
return {
ok: false,
code: -1,
msg: error instanceof Error ? error.message : 'Unknown error',
};
}
}
/**
* 发送项目创建通知
*/
export async function notifyProjectCreated(projectName: string, goal: string): Promise<void> {
await sendFeishuMessage({
text: `🚀 新项目已创建\n\n项目${projectName}\n目标${goal}\n\n请及时查看并确认项目章程。`,
});
}
/**
* 发送里程碑提醒
*/
export async function notifyMilestoneReminder(milestoneName: string, targetDate: string): Promise<void> {
await sendFeishuMessage({
text: `⏰ 里程碑提醒\n\n里程碑「${milestoneName}」即将到期\n目标日期${targetDate}\n\n请确认进度是否正常。`,
});
}
/**
* 发送风险预警
*/
export async function notifyRiskAlert(riskDesc: string, priority: number): Promise<void> {
const level = priority >= 15 ? '🔴 高' : priority >= 10 ? '🟡 中' : '🟢 低';
await sendFeishuMessage({
text: `⚠️ 风险预警\n\n风险${riskDesc}\n优先级${level}${priority}分)\n\n请评估并制定应对措施。`,
});
}
/**
* 发送决策请求
*/
export async function notifyDecisionRequired(title: string, options: string[]): Promise<void> {
const optionList = options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
await sendFeishuMessage({
text: `🔑 需要您的决策\n\n${title}\n\n选项\n${optionList}\n\n请回复选项编号。`,
});
}