02-taskManager.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * 云函数:taskManager(任务管理)
  3. * 替代:GET/POST/PUT/DELETE /api/tasks[/:id]
  4. * actions: list | get | create | update | delete
  5. */
  6. async function handler(request, response) {
  7. try {
  8. await Psql.query(`
  9. CREATE TABLE IF NOT EXISTS "VideoflowTask" (
  10. "objectId" VARCHAR(50) PRIMARY KEY,
  11. "bizId" VARCHAR(255),
  12. "data" JSONB NOT NULL DEFAULT '{}',
  13. "userId" VARCHAR(255) DEFAULT '',
  14. "type" VARCHAR(50) DEFAULT '',
  15. "status" VARCHAR(50) DEFAULT '',
  16. "createdAt" TIMESTAMPTZ DEFAULT NOW(),
  17. "updatedAt" TIMESTAMPTZ DEFAULT NOW()
  18. )
  19. `);
  20. await Psql.query(`CREATE UNIQUE INDEX IF NOT EXISTS idx_videoflow_task_biz ON "VideoflowTask" ("bizId")`);
  21. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_videoflow_task_user ON "VideoflowTask" ("userId")`);
  22. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_videoflow_task_status ON "VideoflowTask" ("status")`);
  23. const action = pickParam(request, 'action') || 'list';
  24. const userId = pickParam(request, 'userId') || '';
  25. if (action === 'list') {
  26. const conds = [], params = [];
  27. if (userId) { params.push(userId); conds.push(`"userId" = $${params.length}`); }
  28. const status = pickParam(request, 'status');
  29. if (status) { params.push(status); conds.push(`"status" = $${params.length}`); }
  30. const type = pickParam(request, 'type');
  31. if (type) { params.push(type); conds.push(`"type" = $${params.length}`); }
  32. const limit = Math.min(parseInt(pickParam(request, 'limit') || '500'), 1000);
  33. const where = conds.length ? `WHERE ${conds.join(' AND ')}` : '';
  34. params.push(limit);
  35. const rows = await Psql.query(
  36. `SELECT * FROM "VideoflowTask" ${where} ORDER BY "createdAt" DESC LIMIT $${params.length}`,
  37. params
  38. );
  39. response.json({ code: 200, success: true, data: rows.map(rowToObj) });
  40. return;
  41. }
  42. if (action === 'get') {
  43. const id = pickParam(request, 'id', 'taskId', 'bizId');
  44. if (!id) return response.json({ code: 400, success: false, error: '缺少 id' });
  45. const rows = await Psql.query(`SELECT * FROM "VideoflowTask" WHERE "bizId" = $1 LIMIT 1`, [id]);
  46. if (!rows.length) return response.json({ code: 404, success: false, error: '未找到任务' });
  47. response.json({ code: 200, success: true, data: rowToObj(rows[0]) });
  48. return;
  49. }
  50. if (action === 'create') {
  51. const body = pickParam(request, 'task', 'data') || request.body || {};
  52. const bizId = body.id || body.bizId || generateId();
  53. const merged = { ...body, id: bizId, created_at: body.created_at || new Date().toISOString(), updated_at: new Date().toISOString() };
  54. const objectId = generateId();
  55. await Psql.query(
  56. `INSERT INTO "VideoflowTask" ("objectId","bizId","data","userId","type","status")
  57. VALUES ($1,$2,$3,$4,$5,$6)
  58. ON CONFLICT ("bizId") DO UPDATE SET "data"=$3, "type"=$5, "status"=$6, "updatedAt"=NOW()`,
  59. [objectId, bizId, JSON.stringify(merged), userId, merged.type || '', merged.status || '']
  60. );
  61. response.json({ code: 200, success: true, data: merged });
  62. return;
  63. }
  64. if (action === 'update') {
  65. const id = pickParam(request, 'id', 'taskId', 'bizId');
  66. if (!id) return response.json({ code: 400, success: false, error: '缺少 id' });
  67. const patch = pickParam(request, 'task', 'data', 'patch') || request.body || {};
  68. const cur = await Psql.query(`SELECT * FROM "VideoflowTask" WHERE "bizId" = $1 LIMIT 1`, [id]);
  69. const now = new Date().toISOString();
  70. const previous = cur.length ? rowToObj(cur[0]) : {};
  71. const merged = {
  72. ...previous,
  73. ...patch,
  74. id,
  75. bizId: patch.bizId || patch.id || id,
  76. created_at: previous.created_at || patch.created_at || now,
  77. updated_at: now,
  78. };
  79. if (cur.length) {
  80. await Psql.query(
  81. `UPDATE "VideoflowTask" SET "data"=$1, "type"=$2, "status"=$3, "updatedAt"=NOW() WHERE "bizId"=$4`,
  82. [JSON.stringify(merged), merged.type || '', merged.status || '', id]
  83. );
  84. } else {
  85. await Psql.query(
  86. `INSERT INTO "VideoflowTask" ("objectId","bizId","data","userId","type","status")
  87. VALUES ($1,$2,$3,$4,$5,$6)
  88. ON CONFLICT ("bizId") DO UPDATE SET "data"=$3, "type"=$5, "status"=$6, "updatedAt"=NOW()`,
  89. [generateId(), id, JSON.stringify(merged), userId || merged.userId || '', merged.type || '', merged.status || '']
  90. );
  91. }
  92. response.json({ code: 200, success: true, data: merged });
  93. return;
  94. }
  95. if (action === 'update') {
  96. const id = pickParam(request, 'id', 'taskId', 'bizId');
  97. if (!id) return response.json({ code: 400, success: false, error: '缺少 id' });
  98. const patch = pickParam(request, 'task', 'data', 'patch') || request.body || {};
  99. const cur = await Psql.query(`SELECT * FROM "VideoflowTask" WHERE "bizId" = $1 LIMIT 1`, [id]);
  100. if (!cur.length) return response.json({ code: 404, success: false, error: '未找到任务' });
  101. const merged = { ...rowToObj(cur[0]), ...patch, updated_at: new Date().toISOString() };
  102. await Psql.query(
  103. `UPDATE "VideoflowTask" SET "data"=$1, "type"=$2, "status"=$3, "updatedAt"=NOW() WHERE "bizId"=$4`,
  104. [JSON.stringify(merged), merged.type || '', merged.status || '', id]
  105. );
  106. response.json({ code: 200, success: true, data: merged });
  107. return;
  108. }
  109. if (action === 'delete') {
  110. const id = pickParam(request, 'id', 'taskId', 'bizId');
  111. if (!id) return response.json({ code: 400, success: false, error: '缺少 id' });
  112. const r = await Psql.query(`DELETE FROM "VideoflowTask" WHERE "bizId" = $1 RETURNING "bizId"`, [id]);
  113. response.json({ code: 200, success: true, data: { id, deleted: r.length > 0 } });
  114. return;
  115. }
  116. response.json({ code: 400, success: false, error: `未知 action: ${action}` });
  117. } catch (error) {
  118. console.error('❌ taskManager 失败:', error.message);
  119. response.json({ code: 500, success: false, error: error.message });
  120. }
  121. }
  122. function rowToObj(row) {
  123. const data = typeof row.data === 'string' ? JSON.parse(row.data) : (row.data || {});
  124. return { ...data, objectId: row.objectId, createdAt: row.createdAt, updatedAt: row.updatedAt };
  125. }
  126. function pickParam(request, ...names) {
  127. const sources = [request.params, request.body, request];
  128. for (const src of sources) {
  129. if (!src || typeof src !== 'object') continue;
  130. for (const n of names) {
  131. const v = src[n];
  132. if (v !== undefined && v !== null && v !== '') return v;
  133. }
  134. }
  135. return null;
  136. }
  137. function generateId() {
  138. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  139. let s = '';
  140. for (let i = 0; i < 10; i++) s += chars.charAt(Math.floor(Math.random() * chars.length));
  141. return s;
  142. }