03-historyManager.js 4.1 KB

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