05-remixManager.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /**
  2. * 云函数:remixManager(视频重塑记录)
  3. * 替代:GET/POST/DELETE /api/remixes[/:videoId[/:remixId]]
  4. * actions:
  5. * listAll → 所有视频的重塑汇总 { [videoId]: Remix[] }
  6. * listByVideo → 某视频的所有重塑
  7. * upsert → 创建或更新一条
  8. * delete → 删除一条
  9. */
  10. async function handler(request, response) {
  11. try {
  12. await Psql.query(`
  13. CREATE TABLE IF NOT EXISTS "Remix" (
  14. "objectId" VARCHAR(50) PRIMARY KEY,
  15. "videoId" VARCHAR(255) NOT NULL,
  16. "remixId" VARCHAR(255) NOT NULL,
  17. "data" JSONB NOT NULL DEFAULT '{}',
  18. "userId" VARCHAR(255) DEFAULT '',
  19. "createdAt" TIMESTAMPTZ DEFAULT NOW(),
  20. "updatedAt" TIMESTAMPTZ DEFAULT NOW()
  21. )
  22. `);
  23. await Psql.query(`CREATE UNIQUE INDEX IF NOT EXISTS idx_remix_pair ON "Remix" ("videoId","remixId")`);
  24. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_remix_video ON "Remix" ("videoId")`);
  25. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_remix_user ON "Remix" ("userId")`);
  26. const action = pickParam(request, 'action') || 'listAll';
  27. const userId = pickParam(request, 'userId') || '';
  28. if (action === 'listAll') {
  29. const conds = [], params = [];
  30. if (userId) { params.push(userId); conds.push(`"userId" = $${params.length}`); }
  31. const where = conds.length ? `WHERE ${conds.join(' AND ')}` : '';
  32. const rows = await Psql.query(
  33. `SELECT * FROM "Remix" ${where} ORDER BY "createdAt" DESC LIMIT 5000`,
  34. params
  35. );
  36. const grouped = {};
  37. for (const r of rows) {
  38. if (!grouped[r.videoId]) grouped[r.videoId] = [];
  39. grouped[r.videoId].push(rowToObj(r));
  40. }
  41. response.json({ code: 200, success: true, data: grouped });
  42. return;
  43. }
  44. if (action === 'listByVideo') {
  45. const videoId = pickParam(request, 'videoId');
  46. if (!videoId) return response.json({ code: 400, success: false, error: '缺少 videoId' });
  47. const rows = userId
  48. ? await Psql.query(`SELECT * FROM "Remix" WHERE "videoId" = $1 AND "userId" = $2 ORDER BY "createdAt" DESC`, [videoId, userId])
  49. : await Psql.query(`SELECT * FROM "Remix" WHERE "videoId" = $1 ORDER BY "createdAt" DESC`, [videoId]);
  50. response.json({ code: 200, success: true, data: rows.map(rowToObj) });
  51. return;
  52. }
  53. if (action === 'upsert') {
  54. const videoId = pickParam(request, 'videoId');
  55. const body = pickParam(request, 'remix', 'data') || request.body || {};
  56. const remixId = body.remixId || pickParam(request, 'remixId');
  57. if (!videoId || !remixId) {
  58. return response.json({ code: 400, success: false, error: '缺少 videoId 或 remixId' });
  59. }
  60. const now = new Date().toISOString();
  61. const merged = { ...body, remixId, videoId, updated_at: now };
  62. const exists = userId
  63. ? await Psql.query(`SELECT "objectId" FROM "Remix" WHERE "videoId"=$1 AND "remixId"=$2 AND "userId"=$3`, [videoId, remixId, userId])
  64. : await Psql.query(`SELECT "objectId" FROM "Remix" WHERE "videoId"=$1 AND "remixId"=$2`, [videoId, remixId]);
  65. if (exists.length) {
  66. await Psql.query(
  67. userId ? `UPDATE "Remix" SET "data"=$1, "updatedAt"=NOW() WHERE "videoId"=$2 AND "remixId"=$3 AND "userId"=$4` : `UPDATE "Remix" SET "data"=$1, "updatedAt"=NOW() WHERE "videoId"=$2 AND "remixId"=$3`,
  68. userId ? [JSON.stringify(merged), videoId, remixId, userId] : [JSON.stringify(merged), videoId, remixId]
  69. );
  70. } else {
  71. merged.created_at = now;
  72. const objectId = generateId();
  73. await Psql.query(
  74. `INSERT INTO "Remix" ("objectId","videoId","remixId","data","userId") VALUES ($1,$2,$3,$4,$5)`,
  75. [objectId, videoId, remixId, JSON.stringify(merged), userId]
  76. );
  77. }
  78. response.json({ code: 200, success: true, data: merged });
  79. return;
  80. }
  81. if (action === 'delete') {
  82. const videoId = pickParam(request, 'videoId');
  83. const remixId = pickParam(request, 'remixId');
  84. if (!videoId || !remixId) {
  85. return response.json({ code: 400, success: false, error: '缺少 videoId 或 remixId' });
  86. }
  87. const r = await Psql.query(
  88. userId ? `DELETE FROM "Remix" WHERE "videoId"=$1 AND "remixId"=$2 AND "userId"=$3 RETURNING "objectId"` : `DELETE FROM "Remix" WHERE "videoId"=$1 AND "remixId"=$2 RETURNING "objectId"`,
  89. userId ? [videoId, remixId, userId] : [videoId, remixId]
  90. );
  91. response.json({ code: 200, success: true, data: { videoId, remixId, deleted: r.length > 0 } });
  92. return;
  93. }
  94. response.json({ code: 400, success: false, error: `未知 action: ${action}` });
  95. } catch (error) {
  96. console.error('❌ remixManager 失败:', error.message);
  97. response.json({ code: 500, success: false, error: error.message });
  98. }
  99. }
  100. function rowToObj(row) {
  101. const data = typeof row.data === 'string' ? JSON.parse(row.data) : (row.data || {});
  102. return { ...data, objectId: row.objectId, createdAt: row.createdAt, updatedAt: row.updatedAt };
  103. }
  104. function pickParam(request, ...names) {
  105. const sources = [request.params, request.body, request];
  106. for (const src of sources) {
  107. if (!src || typeof src !== 'object') continue;
  108. for (const n of names) {
  109. const v = src[n];
  110. if (v !== undefined && v !== null && v !== '') return v;
  111. }
  112. }
  113. return null;
  114. }
  115. function generateId() {
  116. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  117. let s = '';
  118. for (let i = 0; i < 10; i++) s += chars.charAt(Math.floor(Math.random() * chars.length));
  119. return s;
  120. }