需求:
1.用户使用AI转录时首次任务的等待时长
2.如果超过30s等待时长则需要进行飞书报警提示
实现:
1.使用的Keystone 6搭建的一个后台管理系统,第一步连接数据库使用的knex映射mysql来查询
import knex, { Knex } from "knex";
const getConnectionConfig = (db: string): Knex.StaticConnectionConfig | Knex.ConnectionConfigProvider => {
return {
host: 'xxxxxxxxxx',
port: 3306,
user: 'xxxx',
password: 'xxxx',
database: db
}
}
const connShire = knex({
client: 'mysql2',
connection: getConnectionConfig('shire'),
});
const connManager = knex({
client: 'mysql2',
connection: getConnectionConfig('manger')
});
export {
connManager, connShire
}
2.查询逻辑
- 以当前时间为起点对前24小时内创建的用户作为新用户
- 来查询它的第一条转录task任务的id
- 拿到id后查询任务表对应的任务状态是否为wail等待并获取它的时长
- 返回用户的id和时长 如果大于30s 则进行报警 接口如下↓↓↓↓↓↓
import { connManager } from "./connections";
interface WaitParams {
twentyFourHoursAgo?: Date;
}
export async function WaitTasks(params: WaitParams) {
const userTask = connManager('asr_user_tasks');
const taskSteps = connManager('asr_task_steps');
let twentyFourHoursAgo = params.twentyFourHoursAgo ?? new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
// 查找每个用户在当前时间前24小时内创建的最小 task_id
const waitTaskList = await userTask
.select('user_id')
.min('task_id as first_task_id')
.where('created_at', '>=', twentyFourHoursAgo)
.groupBy('user_id')
.then(results => {
const firstTaskIds = results.map(row=>row.first_task_id);
return taskSteps.select('task_id','cost','created_at').whereIn('task_id',firstTaskIds).andWhere('step','wait')
})
.then(taskStepsResults=>{
const alertTasks:Array<{task_id:string,step_cost:string,created_at:string}> = [];
taskStepsResults.forEach(step=>{
if(step.cost>30){
alertTasks.push({ task_id: step.task_id, step_cost: step.cost,created_at: step.created_at });
}
});
return alertTasks;
})
.catch(err => {
console.error(err);
})
return waitTaskList;
}
并在routers下创建接口
app.get('/api/clipto/xxx/xxx-xxx-xxx', async (req, res, next) => {
try {
const twentyFourHoursAgo = req.query.date ? new Date(req.query.date as string) : undefined
const waitTaskList = await WaitTasks({twentyFourHoursAgo});
res.status(200).json(waitTaskList);
} catch (e) {
res.status(500).send(e);
}
});
3.获取到报警任务列表并定义好接口后,在服务启动时进行定时器轮询 使用的node-schedule
- 启动后运行定时器 对报警的任务进行去重, 每1分钟获取一次 , 使用set存储 每天凌晨清除缓存 避免内存堵塞 服务器崩溃
//scheduledTtask.ts
import schedule from 'node-schedule';
import { WaitTasks } from './waitTask';
const interval = '*/1 * * * *';
const sentAlerts = new Set();
//每天凌晨清除set缓存
schedule.scheduleJob('0 0 */1 * *', () => {
sentAlerts.clear();
console.log('已清空已发送集合');
});
async function sendFeishuAlert(task: { task_id: string, step_cost: string }) {
const alertKey = `${task.task_id}-${task.step_cost}`;
if (sentAlerts.has(alertKey)) {
return;
}
const webhookUrl = 'https://www.feishu.cn/flow/api/xxxxxx/xxxxxxx';
const message = {
errorMsg: `Task ${task.task_id} has a wait time of ${task.step_cost} seconds. Triggering alert.`,
};
try {
await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
sentAlerts.add(alertKey);
} catch (error) {
console.error('Error sending alert to Feishu:', error);
}
}
// 定义轮询函数
async function pollWaitTasks() {
try {
const waitTaskList = await WaitTasks({ twentyFourHoursAgo: undefined });
if (waitTaskList) {
waitTaskList.forEach(task => {
if (Number(task.step_cost) > 30) {
sendFeishuAlert(task);
}
});
}
} catch (e) {
console.error('Error:', e);
}
}
//启动
schedule.scheduleJob(interval, pollWaitTasks);
//在keystone.ts中引入即可