03-historyManager.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * 云函数:historyManager(历史记录)
  3. * 替代:GET/POST/DELETE /api/history[/:id]
  4. * actions: list | create | delete | clearAll
  5. */
  6. async function handler(request, response) {
  7. try {
  8. await Psql.query(`
  9. CREATE TABLE IF NOT EXISTS "History" (
  10. "objectId" VARCHAR(50) PRIMARY KEY,
  11. "bizId" VARCHAR(255),
  12. "data" JSONB NOT NULL DEFAULT '{}',
  13. "userId" VARCHAR(255) DEFAULT '',
  14. "action" VARCHAR(100) DEFAULT '',
  15. "createdAt" TIMESTAMPTZ DEFAULT NOW()
  16. )
  17. `);
  18. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_history_user ON "History" ("userId")`);
  19. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_history_created ON "History" ("createdAt" DESC)`);
  20. const action = pickParam(request, 'action') || 'list';
  21. const userId = pickParam(request, 'userId') || '';
  22. if (action === 'list') {
  23. const conds = [], params = [];
  24. if (userId) { params.push(userId); conds.push(`"userId" = $${params.length}`); }
  25. const limit = Math.min(parseInt(pickParam(request, 'limit') || '500'), 2000);
  26. const where = conds.length ? `WHERE ${conds.join(' AND ')}` : '';
  27. params.push(limit);
  28. const rows = await Psql.query(
  29. `SELECT * FROM "History" ${where} ORDER BY "createdAt" DESC LIMIT $${params.length}`,
  30. params
  31. );
  32. response.json({ code: 200, success: true, data: rows.map(rowToObj) });
  33. return;
  34. }
  35. if (action === 'create') {
  36. const body = pickParam(request, 'entry', 'data') || request.body || {};
  37. const bizId = body.id || body.bizId || generateId();
  38. const merged = { ...body, id: bizId, created_at: body.created_at || new Date().toISOString() };
  39. const objectId = generateId();
  40. await Psql.query(
  41. `INSERT INTO "History" ("objectId","bizId","data","userId","action") VALUES ($1,$2,$3,$4,$5)`,
  42. [objectId, bizId, JSON.stringify(merged), userId, body.action || body.type || '']
  43. );
  44. response.json({ code: 200, success: true, data: merged });
  45. return;
  46. }
  47. if (action === 'delete') {
  48. const id = pickParam(request, 'id', 'bizId');
  49. if (!id) return response.json({ code: 400, success: false, error: '缺少 id' });
  50. const r = userId
  51. ? await Psql.query(`DELETE FROM "History" WHERE "bizId" = $1 AND "userId" = $2 RETURNING "bizId"`, [id, userId])
  52. : await Psql.query(`DELETE FROM "History" WHERE "bizId" = $1 RETURNING "bizId"`, [id]);
  53. response.json({ code: 200, success: true, data: { id, deleted: r.length > 0 } });
  54. return;
  55. }
  56. if (action === 'clearAll') {
  57. if (userId) {
  58. await Psql.query(`DELETE FROM "History" WHERE "userId" = $1`, [userId]);
  59. } else {
  60. await Psql.query(`DELETE FROM "History"`);
  61. }
  62. response.json({ code: 200, success: true, message: '已清空历史' });
  63. return;
  64. }
  65. response.json({ code: 400, success: false, error: `未知 action: ${action}` });
  66. } catch (error) {
  67. console.error('❌ historyManager 失败:', error.message);
  68. response.json({ code: 500, success: false, error: error.message });
  69. }
  70. }
  71. function rowToObj(row) {
  72. const data = typeof row.data === 'string' ? JSON.parse(row.data) : (row.data || {});
  73. return { ...data, objectId: row.objectId, createdAt: row.createdAt };
  74. }
  75. function pickParam(request, ...names) {
  76. const sources = [request.params, request.body, request];
  77. for (const src of sources) {
  78. if (!src || typeof src !== 'object') continue;
  79. for (const n of names) {
  80. const v = src[n];
  81. if (v !== undefined && v !== null && v !== '') return v;
  82. }
  83. }
  84. return null;
  85. }
  86. function generateId() {
  87. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  88. let s = '';
  89. for (let i = 0; i < 10; i++) s += chars.charAt(Math.floor(Math.random() * chars.length));
  90. return s;
  91. }