creatives.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.creativesRouter = creativesRouter;
  7. const express_1 = require("express");
  8. const uuid_1 = require("uuid");
  9. const fs_1 = __importDefault(require("fs"));
  10. const path_1 = __importDefault(require("path"));
  11. function creativesRouter(db, storageDir, onThemeSaved) {
  12. const router = (0, express_1.Router)();
  13. // GET /api/v1/creatives — 创意列表
  14. router.get("/", (req, res) => {
  15. const { status, page = "1", limit = "20" } = req.query;
  16. const offset = (Number(page) - 1) * Number(limit);
  17. let sql = `
  18. SELECT c.*, t.name as template_name
  19. FROM creatives c
  20. JOIN templates t ON c.template_id = t.id
  21. `;
  22. const params = [];
  23. if (status) {
  24. sql += " WHERE c.status = ?";
  25. params.push(status);
  26. }
  27. sql += " ORDER BY c.updated_at DESC LIMIT ? OFFSET ?";
  28. params.push(Number(limit), offset);
  29. const rows = db.prepare(sql).all(...params);
  30. const data = rows.map((row) => ({
  31. id: row.id,
  32. name: row.name,
  33. templateId: row.template_id,
  34. templateName: row.template_name,
  35. status: row.status,
  36. createdAt: row.created_at,
  37. updatedAt: row.updated_at,
  38. }));
  39. res.json({ data });
  40. });
  41. // POST /api/v1/creatives — 新建创意
  42. router.post("/", (req, res) => {
  43. const { name, templateId } = req.body;
  44. if (!name || !templateId) {
  45. res.status(400).json({
  46. error: { message: "name and templateId are required" },
  47. });
  48. return;
  49. }
  50. // 校验模板存在
  51. const template = db
  52. .prepare("SELECT id FROM templates WHERE id = ?")
  53. .get(templateId);
  54. if (!template) {
  55. res.status(400).json({
  56. error: { message: `Template '${templateId}' not found` },
  57. });
  58. return;
  59. }
  60. const id = (0, uuid_1.v4)();
  61. db.prepare("INSERT INTO creatives (id, name, template_id) VALUES (?, ?, ?)").run(id, name, templateId);
  62. const creative = db.prepare("SELECT * FROM creatives WHERE id = ?").get(id);
  63. res.status(201).json({
  64. data: {
  65. id: creative.id,
  66. name: creative.name,
  67. templateId: creative.template_id,
  68. status: creative.status,
  69. theme: JSON.parse(creative.theme),
  70. createdAt: creative.created_at,
  71. updatedAt: creative.updated_at,
  72. },
  73. });
  74. });
  75. // GET /api/v1/creatives/:id — 创意详情
  76. router.get("/:id", (req, res) => {
  77. const creative = db
  78. .prepare(`SELECT c.*, t.name as template_name
  79. FROM creatives c
  80. JOIN templates t ON c.template_id = t.id
  81. WHERE c.id = ?`)
  82. .get(req.params.id);
  83. if (!creative) {
  84. res.status(404).json({ error: { message: "Creative not found" } });
  85. return;
  86. }
  87. // 获取素材列表
  88. const assets = db
  89. .prepare("SELECT * FROM creative_assets WHERE creative_id = ?")
  90. .all(creative.id);
  91. // 获取最近构建
  92. const recentBuilds = db
  93. .prepare("SELECT id, status, platforms, results, error_log, started_at, finished_at, created_at FROM builds WHERE creative_id = ? ORDER BY created_at DESC LIMIT 5")
  94. .all(creative.id);
  95. res.json({
  96. data: {
  97. id: creative.id,
  98. name: creative.name,
  99. templateId: creative.template_id,
  100. template: {
  101. id: creative.template_id,
  102. name: creative.template_name,
  103. },
  104. status: creative.status,
  105. theme: JSON.parse(creative.theme),
  106. assets: assets.map((a) => ({
  107. key: a.file_key,
  108. fileName: a.file_name,
  109. fileSize: a.file_size,
  110. isRequired: !!a.is_required,
  111. })),
  112. recentBuilds: recentBuilds.map((b) => ({
  113. id: b.id,
  114. status: b.status,
  115. platforms: JSON.parse(b.platforms),
  116. results: b.results ? JSON.parse(b.results) : null,
  117. errorLog: b.error_log,
  118. startedAt: b.started_at,
  119. finishedAt: b.finished_at,
  120. createdAt: b.created_at,
  121. })),
  122. createdAt: creative.created_at,
  123. updatedAt: creative.updated_at,
  124. },
  125. });
  126. });
  127. // PATCH /api/v1/creatives/:id — 更新创意
  128. router.patch("/:id", (req, res) => {
  129. const creative = db
  130. .prepare("SELECT * FROM creatives WHERE id = ?")
  131. .get(req.params.id);
  132. if (!creative) {
  133. res.status(404).json({ error: { message: "Creative not found" } });
  134. return;
  135. }
  136. const { name, theme } = req.body;
  137. if (name !== undefined) {
  138. db.prepare("UPDATE creatives SET name = ?, updated_at = datetime('now') WHERE id = ?")
  139. .run(name, req.params.id);
  140. }
  141. if (theme !== undefined) {
  142. db.prepare("UPDATE creatives SET theme = ?, updated_at = datetime('now') WHERE id = ?")
  143. .run(JSON.stringify(theme), req.params.id);
  144. // 通知预览服务更新配置
  145. onThemeSaved?.(req.params.id, theme, storageDir);
  146. }
  147. // 返回更新后的数据
  148. const updated = db
  149. .prepare("SELECT * FROM creatives WHERE id = ?")
  150. .get(req.params.id);
  151. res.json({
  152. data: {
  153. id: updated.id,
  154. name: updated.name,
  155. templateId: updated.template_id,
  156. status: updated.status,
  157. theme: JSON.parse(updated.theme),
  158. updatedAt: updated.updated_at,
  159. },
  160. });
  161. });
  162. // DELETE /api/v1/creatives/:id — 删除创意
  163. router.delete("/:id", (req, res) => {
  164. const creative = db
  165. .prepare("SELECT * FROM creatives WHERE id = ?")
  166. .get(req.params.id);
  167. if (!creative) {
  168. res.status(404).json({ error: { message: "Creative not found" } });
  169. return;
  170. }
  171. // 删除磁盘文件
  172. const creativeDir = path_1.default.join(storageDir, "creatives", creative.id);
  173. if (fs_1.default.existsSync(creativeDir)) {
  174. fs_1.default.rmSync(creativeDir, { recursive: true, force: true });
  175. }
  176. // 级联删除数据库记录(foreign key ON DELETE CASCADE)
  177. db.prepare("DELETE FROM creatives WHERE id = ?").run(req.params.id);
  178. res.json({ data: { id: req.params.id, deleted: true } });
  179. });
  180. return router;
  181. }
  182. //# sourceMappingURL=creatives.js.map