toolsCallLog.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. const express = require("express");
  2. const router = express.Router();
  3. const { executeQuery } = require("../config/database");
  4. // 获取工具调用日志列表
  5. router.get("/", async (req, res) => {
  6. try {
  7. const { page = 1, pageSize = 10, user, mcp_tools_name, status, startTime, endTime } = req.query;
  8. const offset = (parseInt(page) - 1) * parseInt(pageSize);
  9. // 构建查询条件
  10. let whereConditions = [];
  11. let queryParams = [];
  12. if (user) {
  13. whereConditions.push("user LIKE ?");
  14. queryParams.push(`%${user}%`);
  15. }
  16. if (mcp_tools_name) {
  17. whereConditions.push("mcp_tools_name LIKE ?");
  18. queryParams.push(`%${mcp_tools_name}%`);
  19. }
  20. if (status) {
  21. if (status === "failure") {
  22. // 将“失败”视为非成功的所有状态,避免仅限于字符串 'failure'
  23. whereConditions.push("status <> 'success'");
  24. } else {
  25. whereConditions.push("status = ?");
  26. queryParams.push(status);
  27. }
  28. }
  29. if (startTime) {
  30. whereConditions.push("call_timestamp >= ?");
  31. queryParams.push(parseInt(startTime));
  32. }
  33. if (endTime) {
  34. whereConditions.push("call_timestamp <= ?");
  35. queryParams.push(parseInt(endTime));
  36. }
  37. const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : "";
  38. // 获取总数
  39. const countQuery = `SELECT COUNT(*) as total FROM tools_call_log ${whereClause}`;
  40. const countResult = await executeQuery(countQuery, queryParams);
  41. const total = countResult[0]?.total || 0;
  42. // 获取列表数据
  43. const listQuery = `
  44. SELECT id, user, token, mcp_tools_name, status, fail_msg,
  45. call_timestamp, finish_timestamp
  46. FROM tools_call_log
  47. ${whereClause}
  48. ORDER BY call_timestamp DESC
  49. LIMIT ? OFFSET ?
  50. `;
  51. const listParams = [...queryParams, parseInt(pageSize), offset];
  52. const list = await executeQuery(listQuery, listParams);
  53. res.json({
  54. data: list,
  55. total,
  56. page: parseInt(page),
  57. pageSize: parseInt(pageSize),
  58. });
  59. } catch (error) {
  60. console.error("获取工具调用日志列表失败:", error);
  61. res.status(500).json({
  62. success: false,
  63. error: "获取工具调用日志列表失败",
  64. });
  65. }
  66. });
  67. // 获取工具调用日志详情(仅匹配数字ID,避免与 /stats 等命名路由冲突)
  68. router.get("/:id(\\d+)", async (req, res) => {
  69. try {
  70. const { id } = req.params;
  71. const query = `
  72. SELECT id, user, token, mcp_tools_name, request_params, status,
  73. response, fail_msg, call_timestamp, finish_timestamp
  74. FROM tools_call_log
  75. WHERE id = ?
  76. `;
  77. const result = await executeQuery(query, [id]);
  78. if (result.length === 0) {
  79. return res.status(404).json({
  80. success: false,
  81. error: "工具调用日志不存在",
  82. });
  83. }
  84. res.json(result[0]);
  85. } catch (error) {
  86. console.error("获取工具调用日志详情失败:", error);
  87. res.status(500).json({
  88. success: false,
  89. error: "获取工具调用日志详情失败",
  90. });
  91. }
  92. });
  93. // 获取统计信息:支持与列表一致的筛选条件
  94. router.get("/stats", async (req, res) => {
  95. try {
  96. const { user, mcp_tools_name, status, startTime, endTime } = req.query;
  97. let whereConditions = [];
  98. let queryParams = [];
  99. if (user) {
  100. whereConditions.push("user LIKE ?");
  101. queryParams.push(`%${user}%`);
  102. }
  103. if (mcp_tools_name) {
  104. whereConditions.push("mcp_tools_name LIKE ?");
  105. queryParams.push(`%${mcp_tools_name}%`);
  106. }
  107. if (status) {
  108. if (status === "failure") {
  109. whereConditions.push("status <> 'success'");
  110. } else {
  111. whereConditions.push("status = ?");
  112. queryParams.push(status);
  113. }
  114. }
  115. if (startTime) {
  116. whereConditions.push("call_timestamp >= ?");
  117. queryParams.push(parseInt(startTime));
  118. }
  119. if (endTime) {
  120. whereConditions.push("call_timestamp <= ?");
  121. queryParams.push(parseInt(endTime));
  122. }
  123. const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(" AND ")}` : "";
  124. const sql = `
  125. SELECT
  126. COUNT(*) AS total,
  127. SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_count
  128. FROM tools_call_log
  129. ${whereClause}
  130. `;
  131. const rows = await executeQuery(sql, queryParams);
  132. const row = rows && rows[0] ? rows[0] : { total: 0, success_count: 0 };
  133. const total = Number(row.total) || 0;
  134. const successCount = Number(row.success_count) || 0;
  135. const failureCount = total - successCount;
  136. const successRate = total > 0 ? Number(((successCount / total) * 100).toFixed(2)) : 0;
  137. res.json({ total, successCount, failureCount, successRate });
  138. } catch (error) {
  139. console.error("获取工具调用日志统计失败:", error);
  140. res.status(500).json({ error: "获取工具调用日志统计失败" });
  141. }
  142. });
  143. module.exports = router;