Facility.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use think\Db;
  5. use think\File;
  6. use think\Request;
  7. use RecursiveIteratorIterator;
  8. use RecursiveDirectoryIterator;
  9. class Facility extends Api
  10. {
  11. protected $noNeedLogin = ['*'];
  12. protected $noNeedRight = ['*'];
  13. public function getPreviewSubDirs()
  14. {
  15. $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
  16. $baseRelativePath = 'uploads/operate/ai/Preview';
  17. if (!is_dir($baseDir)) {
  18. return json(['code' => 1, 'msg' => '目录不存在']);
  19. }
  20. $version = $this->generateFlexibleDirectoryHash($baseDir);
  21. $cacheKey = 'preview_flexible_dirs_' . $version;
  22. $dirList = cache($cacheKey);
  23. if (!$dirList) {
  24. $dirList = $this->scanFlexibleDirectories($baseDir, $baseRelativePath);
  25. cache($cacheKey, $dirList);
  26. }
  27. return json([
  28. 'code' => 0,
  29. 'msg' => '获取成功',
  30. 'data' => $dirList
  31. ]);
  32. }
  33. private function scanFlexibleDirectories($baseDir, $baseRelativePath)
  34. {
  35. $dirs = [];
  36. $index = 1;
  37. $firstLevelDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
  38. foreach ($firstLevelDirs as $level1Path) {
  39. $secondLevelDirs = glob($level1Path . '/*', GLOB_ONLYDIR);
  40. if ($secondLevelDirs) {
  41. foreach ($secondLevelDirs as $level2Path) {
  42. $dirs = array_merge($dirs, $this->processDir($level2Path, $baseDir, $baseRelativePath, $index));
  43. }
  44. } else {
  45. $dirs = array_merge($dirs, $this->processDir($level1Path, $baseDir, $baseRelativePath, $index));
  46. }
  47. }
  48. usort($dirs, function ($a, $b) {
  49. return $b['id'] - $a['id'];
  50. });
  51. return $dirs;
  52. }
  53. private function processDir($fullPath, $baseDir, $baseRelativePath, &$index)
  54. {
  55. $result = [];
  56. $relativeDir = ltrim(str_replace($baseDir, '', $fullPath), '/');
  57. $ctime = @filectime($fullPath) ?: time();
  58. $imageFiles = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
  59. $originalImageCount = $imageFiles ? count($imageFiles) : 0;
  60. $img_count = Db::name('text_to_image')
  61. ->where('status', 1)
  62. ->where('custom_image_url', '<>', '')
  63. ->where('img_name', '<>', '')
  64. ->whereLike('old_image_url', $baseRelativePath . '/' . $relativeDir . '/%')
  65. ->count();
  66. $queueLog = Db::name('image_task_log')
  67. ->whereLike('file_name', $baseRelativePath . '/' . $relativeDir . '/%')
  68. ->whereLike('log', '%处理中%')
  69. ->order('id', 'desc')
  70. ->find();
  71. if ($img_count === 0 && !$queueLog && $originalImageCount === 0) {
  72. return [];
  73. }
  74. $result[] = [
  75. 'id' => $index++,
  76. 'name' => basename($fullPath),
  77. 'ctime' => $ctime,
  78. 'ctime_text' => date('Y-m-d H:i:s', $ctime),
  79. 'old_img_count' => $originalImageCount,
  80. 'new_image_count' => $img_count,
  81. 'old_image_url' => $baseRelativePath . '/' . $relativeDir,
  82. 'new_image_url' => '/uploads/operate/ai/dall-e/',
  83. 'queueLog_id' => $queueLog['id'] ?? '',
  84. 'queueLog_task_id' => $queueLog['task_id'] ?? '',
  85. 'queueLog_model_name' => $queueLog['model_name'] ?? '',
  86. 'queueLog_model_name_status' => $queueLog ? 1 : 0,
  87. ];
  88. return $result;
  89. }
  90. private function generateFlexibleDirectoryHash($baseDir)
  91. {
  92. $hash = '';
  93. $dirPaths = [];
  94. $firstDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
  95. foreach ($firstDirs as $dir1) {
  96. $subDirs = glob($dir1 . '/*', GLOB_ONLYDIR);
  97. if ($subDirs) {
  98. foreach ($subDirs as $sub) {
  99. $dirPaths[] = $sub;
  100. $hash .= basename($dir1) . '/' . basename($sub) . filemtime($sub);
  101. }
  102. } else {
  103. $dirPaths[] = $dir1;
  104. $hash .= basename($dir1) . filemtime($dir1);
  105. }
  106. }
  107. $baseRelativePath = 'uploads/operate/ai/Preview';
  108. $queueStatusBits = [];
  109. foreach ($dirPaths as $fullPath) {
  110. $relativeDir = ltrim(str_replace($baseDir, '', $fullPath), '/');
  111. $fileNameLike = $baseRelativePath . '/' . $relativeDir . '/%';
  112. // 查询是否存在任何“处理中”的记录
  113. $logs = Db::name('image_task_log')
  114. ->whereLike('file_name', $fileNameLike)
  115. ->whereLike('log', '%处理中%')
  116. ->select();
  117. // 转换为布尔状态再转成位标记(0 或 1)
  118. $queueStatusBits[] = count($logs) > 0 ? '1' : '0';
  119. // 可选:调试打印
  120. // echo "<pre>路径:{$fileNameLike} => 状态:" . (count($logs) > 0 ? '有处理中' : '无') . "</pre>";
  121. }
  122. // 队列状态位图拼接
  123. $queueStatusHash = implode('', $queueStatusBits); // 如:'01001'
  124. $hash .= '_QS_' . md5($queueStatusHash); // 状态稳定扰动,无需 time()
  125. return md5($hash);
  126. }
  127. // private function generateFlexibleDirectoryHash($baseDir)
  128. // {
  129. // $hash = '';
  130. // $firstDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
  131. // foreach ($firstDirs as $dir1) {
  132. // $subDirs = glob($dir1 . '/*', GLOB_ONLYDIR);
  133. // if ($subDirs) {
  134. // foreach ($subDirs as $sub) {
  135. // $hash .= basename($dir1) . '/' . basename($sub) . filemtime($sub);
  136. // }
  137. // } else {
  138. // $hash .= basename($dir1) . filemtime($dir1);
  139. // }
  140. // }
  141. // return md5($hash);
  142. // }
  143. // /**
  144. // * 采用缓存机制
  145. // * 获取原图目录及每个目录下的图片数量(优化版)
  146. // */
  147. // public function getPreviewSubDirs()
  148. // {
  149. // // 1. 设置基础路径
  150. // $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
  151. // $baseRelativePath = 'uploads/operate/ai/Preview';
  152. //
  153. // // 2. 检查目录是否存在
  154. // if (!is_dir($baseDir)) {
  155. // return json(['code' => 1, 'msg' => '目录不存在']);
  156. // }
  157. //
  158. // // 3. 获取目录最后修改时间作为缓存标识
  159. // $cacheKey = 'preview_dirs_' . md5($baseDir);
  160. // $lastModified = filemtime($baseDir);
  161. // $cacheVersionKey = $cacheKey . '_version';
  162. //
  163. // // 4. 检查缓存版本是否匹配
  164. // if (cache($cacheVersionKey) != $lastModified) {
  165. // cache($cacheKey, null);
  166. // cache($cacheVersionKey, $lastModified, 86400);
  167. // }
  168. //
  169. // // 5. 尝试从缓存获取
  170. // if (!$dirList = cache($cacheKey)) {
  171. // // 6. 重新扫描目录
  172. // $dirList = $this->scanDirectories($baseDir, $baseRelativePath);
  173. // cache($cacheKey, $dirList, 86400); // 缓存1天
  174. // }
  175. //
  176. //
  177. // return json([
  178. // 'code' => 0,
  179. // 'msg' => '获取成功',
  180. // 'data' => $dirList
  181. // ]);
  182. // }
  183. //
  184. // /**
  185. // * 扫描目录结构
  186. // */
  187. // private function scanDirectories($baseDir, $baseRelativePath)
  188. // {
  189. //
  190. // $dirs = [];
  191. // $index = 1;
  192. // $processedDirs = [];
  193. //
  194. // $scanDir = function ($dirPath, $relativePath) use (&$scanDir, &$dirs, &$index, &$processedDirs) {
  195. // $items = @scandir($dirPath) ?: [];
  196. // foreach ($items as $item) {
  197. // if ($item === '.' || $item === '..') continue;
  198. //
  199. // $fullPath = $dirPath . '/' . $item;
  200. // $relPath = $relativePath . '/' . $item;
  201. //
  202. // if (is_dir($fullPath)) {
  203. // $dirKey = md5($fullPath);
  204. // if (!isset($processedDirs[$dirKey])) {
  205. // $processedDirs[$dirKey] = true;
  206. // $scanDir($fullPath, $relPath);
  207. // }
  208. // } elseif (preg_match('/\.(jpg|jpeg|png)$/i', $item)) {
  209. // $parentDir = dirname($fullPath);
  210. // $relativeDir = dirname($relPath);
  211. // $key = md5($parentDir);
  212. //
  213. // if (!isset($dirs[$key])) {
  214. // $ctime = @filectime($parentDir) ?: time();
  215. //
  216. // // 数据库查询
  217. // $hasData = Db::name('text_to_image')
  218. // ->where('custom_image_url', '<>', '')
  219. // ->where('img_name', '<>', '')
  220. // ->whereLike('old_image_url', $relativeDir . '/%')
  221. // ->where('status',1)
  222. // ->cache(true, 300)
  223. // ->count();
  224. //
  225. // $queue_logs = Db::name('queue_logs')
  226. // ->whereLike('file_name', $relativeDir . '/%')
  227. // ->group('file_name')
  228. // ->order('id desc')
  229. // ->find();
  230. //
  231. //
  232. //
  233. // $imageFiles = @glob($parentDir . '/*.{jpg,jpeg,png}', GLOB_BRACE);
  234. // $imageCount = $imageFiles ? count($imageFiles) : 0;
  235. //
  236. // $dirs[$key] = [
  237. // 'model_name'=> $queue_logs['model_name'],
  238. // 'id' => $index++,
  239. // 'name' => basename($parentDir),
  240. // 'count' => $hasData,
  241. // 'ctime' => $ctime,
  242. // 'ctime_text' => date('Y-m-d H:i:s', $ctime),
  243. // 'image_count' => $imageCount,
  244. // 'new_image_url' => "/uploads/operate/ai/dall-e/",
  245. // 'old_image_url' => $relativeDir
  246. // ];
  247. // }
  248. // }
  249. // }
  250. // };
  251. //
  252. // $scanDir($baseDir, $baseRelativePath);
  253. //
  254. // // 按ID降序排序
  255. // $dirList = array_values($dirs);
  256. // usort($dirList, function ($a, $b) {
  257. // return $b['id'] - $a['id'];
  258. // });
  259. //
  260. // return $dirList;
  261. // }
  262. /**
  263. * 获取指定目录所有图片(完全实时版本)
  264. */
  265. public function getPreviewimg()
  266. {
  267. $page = (int)$this->request->param('page', 1);
  268. $limit = (int)$this->request->param('limit', 50);
  269. $status = $this->request->param('status', '');
  270. $status_name = $this->request->param('status_name', '');
  271. $relativePath = $this->request->param('path', '');
  272. $basePath = ROOT_PATH . 'public/';
  273. $fullPath = $basePath . $relativePath;
  274. if (!is_dir($fullPath)) {
  275. return json(['code' => 1, 'msg' => '原图目录不存在']);
  276. }
  277. // 构建缓存键与构建锁键(仅缓存文件系统信息)
  278. $hash = md5($relativePath);
  279. $cacheKey = "previewimg_fileinfo_{$hash}";
  280. $lockKey = "previewimg_building_{$hash}";
  281. $cacheExpire = 600; // 10分钟
  282. $cachedFileInfo = cache($cacheKey);
  283. if (!$cachedFileInfo) {
  284. // 防止缓存"惊群效应"
  285. if (!cache($lockKey)) {
  286. cache($lockKey, 1, 60); // 1分钟构建锁
  287. // 获取所有图片文件信息
  288. $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
  289. $fileInfoMap = [];
  290. foreach ($allImages as $imgPath) {
  291. $relative = str_replace('\\', '/', trim(str_replace($basePath, '', $imgPath), '/'));
  292. $info = @getimagesize($imgPath);
  293. $fileInfoMap[$relative] = [
  294. 'width' => $info[0] ?? 0,
  295. 'height' => $info[1] ?? 0,
  296. 'size_kb' => round(filesize($imgPath) / 1024, 2),
  297. 'created_time' => date('Y-m-d H:i:s', filectime($imgPath))
  298. ];
  299. }
  300. // 构建缓存数据(仅文件系统信息)
  301. $cachedFileInfo = [];
  302. foreach (array_keys($fileInfoMap) as $path) {
  303. $cachedFileInfo[] = [
  304. 'path' => $path,
  305. 'info' => $fileInfoMap[$path]
  306. ];
  307. }
  308. // 设置缓存 + 删除构建锁
  309. cache($cacheKey, $cachedFileInfo, $cacheExpire);
  310. cache($lockKey, null);
  311. } else {
  312. // 等待缓存生成
  313. $waitTime = 0;
  314. while (!$cachedFileInfo && $waitTime < 10) {
  315. sleep(1);
  316. $waitTime++;
  317. $cachedFileInfo = cache($cacheKey);
  318. }
  319. if (!$cachedFileInfo) {
  320. return json(['code' => 2, 'msg' => '系统正忙,请稍后重试']);
  321. }
  322. }
  323. }
  324. // 获取所有需要实时查询的路径
  325. $paths = array_column($cachedFileInfo, 'path');
  326. // 实时查询数据库状态信息(单次批量查询)
  327. $dbRecords = Db::name('text_to_image')
  328. ->whereIn('old_image_url', $paths)
  329. ->field('id as img_id, old_image_url, new_image_url, custom_image_url, chinese_description, english_description, img_name, status, status_name')
  330. ->select();
  331. // 实时查询队列状态(单次批量查询)
  332. $queueRecords = Db::name('image_task_log')
  333. ->where('mod_rq', null)
  334. ->whereIn('file_name', $paths)
  335. ->field('file_name, log')
  336. ->select();
  337. // 实时查询same_count(稍后按需查询)
  338. // 构建映射关系
  339. $processedMap = [];
  340. foreach ($dbRecords as $item) {
  341. $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
  342. $processedMap[$key] = $item;
  343. }
  344. $queueMap = [];
  345. foreach ($queueRecords as $q) {
  346. $key = str_replace('\\', '/', trim($q['file_name'], '/'));
  347. $queueMap[$key] = $q['log'];
  348. }
  349. // 合并数据
  350. $mergedData = [];
  351. foreach ($cachedFileInfo as $data) {
  352. $path = $data['path'];
  353. $item = $processedMap[$path] ?? [];
  354. $mergedData[] = [
  355. 'path' => $path,
  356. 'item' => $item,
  357. 'info' => $data['info'],
  358. 'dbStatus' => isset($item['status']) ? (int)$item['status'] : 0,
  359. 'dbStatusName' => $item['status_name'] ?? '',
  360. 'isProcessed' => !empty($item['img_name']) && !empty($item['custom_image_url']),
  361. 'queueStatus' => $queueMap[$path] ?? ''
  362. ];
  363. }
  364. // 筛选状态字段
  365. $filtered = array_filter($mergedData, function ($data) use ($status, $status_name) {
  366. if ($status !== '' && (int)$status !== $data['dbStatus']) return false;
  367. if ($status_name !== '' && $status_name !== $data['dbStatusName']) return false;
  368. return true;
  369. });
  370. // 分页处理
  371. $total = count($filtered);
  372. $paged = array_slice(array_values($filtered), ($page - 1) * $limit, $limit);
  373. // 实时查询当前页的same_count(优化性能)
  374. $pagedPaths = array_column($paged, 'path');
  375. $sameCountMap = [];
  376. if ($pagedPaths) {
  377. $sameCountMap = Db::name('text_to_image')
  378. ->whereIn('old_image_url', $pagedPaths)
  379. ->where('new_image_url', '<>', '')
  380. ->group('old_image_url')
  381. ->column('count(*) as cnt', 'old_image_url');
  382. }
  383. // 构建最终结果
  384. $result = [];
  385. foreach ($paged as $i => $data) {
  386. $path = $data['path'];
  387. $item = $data['item'];
  388. $info = $data['info'];
  389. $result[] = [
  390. 'id' => ($page - 1) * $limit + $i + 1,
  391. 'path' => $path,
  392. // 实时数据
  393. 'status' => $data['dbStatus'],
  394. 'status_name' => $data['dbStatusName'],
  395. 'same_count' => $sameCountMap[$path] ?? 0,
  396. 'is_processed' => $data['isProcessed'] ? 1 : 0,
  397. 'queue_status' => $data['queueStatus'],
  398. 'new_image_url' => $item['new_image_url'] ?? '',
  399. 'custom_image_url' => $item['custom_image_url'] ?? '',
  400. 'chinese_description' => $item['chinese_description'] ?? '',
  401. 'english_description' => $item['english_description'] ?? '',
  402. 'img_name' => $item['img_name'] ?? '',
  403. // 来自缓存
  404. 'width' => $info['width'],
  405. 'height' => $info['height'],
  406. 'created_time' => $info['created_time']
  407. ];
  408. }
  409. return json([
  410. 'code' => 0,
  411. 'msg' => '获取成功',
  412. 'data' => $result,
  413. 'total' => $total,
  414. 'page' => $page,
  415. 'limit' => $limit
  416. ]);
  417. }
  418. /**
  419. * 通过服务器中目录获取对应数据
  420. */
  421. public function getlsit()
  422. {
  423. // 获取前端传入的图片路径参数
  424. $params = $this->request->param('path', '');
  425. // 查询数据库
  426. $res = Db::name('text_to_image')
  427. ->field('id,chinese_description,english_description,new_image_url,custom_image_url,size,old_image_url,img_name,model,imgtoimg_url')
  428. ->where('old_image_url', $params)
  429. ->where('img_name', '<>', '')
  430. ->order('id desc')
  431. ->select();
  432. return json(['code' => 0, 'msg' => '查询成功', 'data' => $res,'count'=>count($res)]);
  433. }
  434. /**
  435. * 图片上传
  436. */
  437. public function ImgUpload()
  438. {
  439. // 处理 CORS OPTIONS 预检请求
  440. if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
  441. header('Access-Control-Allow-Origin: *');
  442. header('Access-Control-Allow-Methods: POST, OPTIONS');
  443. header('Access-Control-Allow-Headers: Content-Type, Authorization');
  444. header('Access-Control-Max-Age: 86400');
  445. exit(204);
  446. }
  447. // 实际请求必须返回 CORS 头
  448. header('Access-Control-Allow-Origin: *');
  449. // 获取上传的文件
  450. $file = request()->file('image');
  451. if ($file) {
  452. // 指定目标目录(你想上传到的目录)
  453. $targetPath = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'operate' . DS . 'ai' . DS . 'Preview';
  454. // 若目录不存在则创建
  455. if (!is_dir($targetPath)) {
  456. mkdir($targetPath, 0755, true);
  457. }
  458. // 移动文件到指定目录,并验证大小/格式
  459. $info = $file->validate([
  460. 'size' => 10485760, // 最大10MB
  461. 'ext' => 'jpg,png'
  462. ])->move($targetPath);
  463. if ($info) {
  464. $fileName = $info->getSaveName();
  465. $imageUrl = '/uploads/operate/ai/Preview/' . str_replace('\\', '/', $fileName);
  466. return json(['code' => 0, 'msg' => '成功', 'data' => ['url' => $imageUrl]]);
  467. } else {
  468. $res = $file->getError();
  469. return json(['code' => 1, 'msg' => '失败', 'data' => $res]);
  470. }
  471. }
  472. return json(['code' => 1, 'msg' => '没有文件上传', 'data' => null]);
  473. }
  474. /**
  475. * 查询模版
  476. */
  477. public function Template(){
  478. $Template = Db::name("template")->where('ids',1)->find();
  479. return json([
  480. 'code' => 0,
  481. 'msg' => '模版',
  482. 'data' => $Template
  483. ]);
  484. }
  485. /**
  486. * 更新模版
  487. */
  488. public function updatetemplate(){
  489. if (Request::instance()->isPost() == false){
  490. $this->error('非法请求');
  491. }
  492. $params = Request::instance()->post();
  493. if (empty($params['textareaContent']) || empty($params['width']) || empty($params['height'])) {
  494. return json(['code' => 1, 'msg' => '参数缺失']);
  495. }
  496. $Template = Db::name("template")
  497. ->where('ids', 1)
  498. ->update([
  499. 'english_content' => $params['english_content'], // 更新文生文模版内容
  500. 'content' => $params['textareaContent'], // 更新图生文模版内容
  501. 'width' => $params['width'], // 更新宽度
  502. 'height' => $params['height'], // 更新宽度
  503. ]);
  504. if ($Template){
  505. return json(['code' => 0, 'msg' => '成功']);
  506. }else{
  507. return json(['code' => 1, 'msg' => '失败']);
  508. }
  509. }
  510. /**
  511. * 打包图片
  512. */
  513. public function packImagess()
  514. {
  515. try {
  516. $params = $this->request->post();
  517. $paths = $params['paths'] ?? [];
  518. if (empty($paths) || !is_array($paths)) {
  519. return json(['code' => 1, 'msg' => '路径参数不能为空或格式不正确']);
  520. }
  521. // 设置基础路径和压缩目录路径
  522. $basePath = ROOT_PATH . 'public/';
  523. $zipDir = $basePath . 'uploads/operate/ai/zip/';
  524. if (!is_dir($zipDir)) {
  525. mkdir($zipDir, 0755, true);
  526. }
  527. // 压缩包文件名及完整路径
  528. $fileName = 'images_' . date('Ymd_His') . '.zip';
  529. $zipPath = $zipDir . $fileName;
  530. // 创建 Zip 文件
  531. $zip = new \ZipArchive();
  532. if ($zip->open($zipPath, \ZipArchive::CREATE) !== TRUE) {
  533. return json(['code' => 1, 'msg' => '无法创建压缩包']);
  534. }
  535. // 添加文件到压缩包
  536. $addCount = 0;
  537. foreach ($paths as $relativePath) {
  538. $relativePath = ltrim($relativePath, '/');
  539. $fullPath = $basePath . $relativePath;
  540. if (file_exists($fullPath)) {
  541. $zip->addFile($fullPath, basename($fullPath)); // 仅保存文件名
  542. $addCount++;
  543. }
  544. }
  545. $zip->close();
  546. if ($addCount === 0) {
  547. return json(['code' => 1, 'msg' => '未找到有效图片,未生成压缩包']);
  548. }
  549. // 返回下载地址(注意路径与保存路径一致)
  550. $downloadUrl = request()->domain() . '/uploads/operate/ai/zip/' . $fileName;
  551. return json([
  552. 'code' => 0,
  553. 'msg' => '打包成功',
  554. 'download_url' => $downloadUrl
  555. ]);
  556. } catch (\Exception $e) {
  557. return json([
  558. 'code' => 1,
  559. 'msg' => '异常错误:' . $e->getMessage()
  560. ]);
  561. }
  562. }
  563. }