routes_charts.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { Hono } from "hono";
  2. import debug from "debug";
  3. import type {
  4. FileLibrary,
  5. FileCategory,
  6. KnowInfo,
  7. ThemeSettings,
  8. } from "./asset/share/types.ts";
  9. import {
  10. EnableStatus,
  11. DeleteStatus,
  12. ThemeMode,
  13. FontSize,
  14. CompactMode,
  15. } from "./asset/share/types.ts";
  16. import type { Variables, WithAuth } from "./app.tsx";
  17. const log = {
  18. api: debug("api:sys"),
  19. };
  20. // 创建图表数据路由
  21. export function createChartRoutes(withAuth: WithAuth) {
  22. const chartRoutes = new Hono<{ Variables: Variables }>();
  23. // 获取用户活跃度图表数据
  24. chartRoutes.get("/user-activity", withAuth, async (c) => {
  25. try {
  26. const apiClient = c.get('apiClient');
  27. // 获取过去30天的数据
  28. const days = 30;
  29. const result = [];
  30. // 当前日期
  31. const currentDate = new Date();
  32. // 生成过去30天的日期范围
  33. for (let i = days - 1; i >= 0; i--) {
  34. const date = new Date();
  35. date.setDate(currentDate.getDate() - i);
  36. // 格式化日期为 YYYY-MM-DD
  37. const formattedDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
  38. // 查询当天的登录次数
  39. const loginCount = await apiClient.database
  40. .table('login_history')
  41. .whereRaw(`DATE(login_time) = ?`, [formattedDate])
  42. .count();
  43. result.push({
  44. date: formattedDate,
  45. count: Number(loginCount),
  46. });
  47. }
  48. return c.json({
  49. message: "获取用户活跃度数据成功",
  50. data: result,
  51. });
  52. } catch (error) {
  53. log.api("获取用户活跃度数据失败:", error);
  54. return c.json({ error: "获取用户活跃度数据失败" }, 500);
  55. }
  56. });
  57. // 获取文件上传统计图表数据
  58. chartRoutes.get("/file-uploads", withAuth, async (c) => {
  59. try {
  60. const apiClient = c.get('apiClient');
  61. // 获取过去12个月的数据
  62. const months = 12;
  63. const result = [];
  64. // 当前日期
  65. const currentDate = new Date();
  66. // 生成过去12个月的月份范围
  67. for (let i = months - 1; i >= 0; i--) {
  68. const date = new Date();
  69. date.setMonth(currentDate.getMonth() - i);
  70. // 获取年月
  71. const year = date.getFullYear();
  72. const month = date.getMonth() + 1;
  73. // 月份标签
  74. const monthLabel = `${year}-${String(month).padStart(2, '0')}`;
  75. // 查询当月的文件上传数量
  76. const fileCount = await apiClient.database
  77. .table('file_library')
  78. .whereRaw(`YEAR(created_at) = ? AND MONTH(created_at) = ?`, [year, month])
  79. .count();
  80. result.push({
  81. month: monthLabel,
  82. count: Number(fileCount),
  83. });
  84. }
  85. return c.json({
  86. message: "获取文件上传统计数据成功",
  87. data: result,
  88. });
  89. } catch (error) {
  90. log.api("获取文件上传统计数据失败:", error);
  91. return c.json({ error: "获取文件上传统计数据失败" }, 500);
  92. }
  93. });
  94. // 获取文件类型分布图表数据
  95. chartRoutes.get("/file-types", withAuth, async (c) => {
  96. try {
  97. const apiClient = c.get('apiClient');
  98. // 查询不同文件类型的数量
  99. const fileTypeStats = await apiClient.database
  100. .table('file_library')
  101. .select('file_type',apiClient.database.raw('count(id) as count'))
  102. .where('is_deleted', DeleteStatus.NOT_DELETED)
  103. .groupBy('file_type');
  104. // 将结果转换为饼图所需格式
  105. const result = fileTypeStats.map(item => ({
  106. type: item.file_type || '未知',
  107. value: Number(item.count),
  108. }));
  109. return c.json({
  110. message: "获取文件类型分布数据成功",
  111. data: result,
  112. });
  113. } catch (error) {
  114. log.api("获取文件类型分布数据失败:", error);
  115. return c.json({ error: "获取文件类型分布数据失败" }, 500);
  116. }
  117. });
  118. // 获取仪表盘概览数据
  119. chartRoutes.get("/dashboard-overview", withAuth, async (c) => {
  120. try {
  121. const apiClient = c.get('apiClient');
  122. // 获取用户总数
  123. const userCount = await apiClient.database
  124. .table('users')
  125. .where('is_deleted', DeleteStatus.NOT_DELETED)
  126. .count();
  127. // 获取文件总数
  128. const fileCount = await apiClient.database
  129. .table('file_library')
  130. .where('is_deleted', DeleteStatus.NOT_DELETED)
  131. .count();
  132. // 获取知识库文章总数
  133. const articleCount = await apiClient.database
  134. .table('know_info')
  135. .where('is_deleted', DeleteStatus.NOT_DELETED)
  136. .count();
  137. // 获取今日登录次数
  138. const today = new Date().toISOString().split('T')[0];
  139. const todayLoginCount = await apiClient.database
  140. .table('login_history')
  141. .whereRaw(`DATE(login_time) = ?`, [today])
  142. .count();
  143. return c.json({
  144. message: "获取仪表盘概览数据成功",
  145. data: {
  146. userCount: Number(userCount),
  147. fileCount: Number(fileCount),
  148. articleCount: Number(articleCount),
  149. todayLoginCount: Number(todayLoginCount),
  150. },
  151. });
  152. } catch (error) {
  153. log.api("获取仪表盘概览数据失败:", error);
  154. return c.json({ error: "获取仪表盘概览数据失败" }, 500);
  155. }
  156. });
  157. return chartRoutes;
  158. }