WorkOrder.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use app\job\ImageJob;
  5. use app\service\ImageService;
  6. use think\App;
  7. use think\Db;
  8. use think\Log;
  9. use think\Queue;
  10. use think\queue\job\Redis;
  11. class WorkOrder extends Api
  12. {
  13. protected $noNeedLogin = ['*'];
  14. protected $noNeedRight = ['*'];
  15. /**
  16. * 出图接口
  17. * 此方法处理图像转换为文本的请求,将图像信息存入队列以供后续处理。
  18. */
  19. public function imageToText()
  20. {
  21. $params = $this->request->param();
  22. $service = new ImageService();
  23. $service->handleImage($params);
  24. $this->success('任务成功提交至队列');
  25. }
  26. /**
  27. * 图生图
  28. * /sdapi/v1/img2img
  29. */
  30. public function imgtowimg()
  31. {
  32. $prompt = $this->request->param('prompt', '');
  33. $denoising = (float)$this->request->param('denoising_strength', 0.2); // 重绘幅度
  34. $scale = (float)$this->request->param('scale', 2.0); // 放大倍数
  35. $modelName = $this->request->param('model', 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]');
  36. //原图路径
  37. $imgRelPath = 'uploads/operate/ai/Preview/arr/0828004096727.png';
  38. $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
  39. if (!file_exists($imgPath)) {
  40. return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
  41. }
  42. list($originW, $originH) = getimagesize($imgPath);
  43. $targetW = intval($originW * $scale);
  44. $targetH = intval($originH * $scale);
  45. // 将原图转为 base64
  46. $imgData = file_get_contents($imgPath);
  47. $base64Img = base64_encode($imgData);
  48. $initImage = 'data:image/png;base64,' . $base64Img;
  49. // 构造请求体
  50. $postData = json_encode([
  51. 'prompt' => $prompt,
  52. 'steps' => 30,
  53. 'cfg_scale' => 7,
  54. 'denoising_strength' => $denoising,
  55. 'width' => $targetW,
  56. 'height' => $targetH,
  57. 'resize_mode' => 1, // 缩放模式
  58. 'inpaint_full_res' => true,
  59. 'inpainting_fill' => 1,
  60. 'init_images' => [$initImage],
  61. 'override_settings' => [
  62. 'sd_model_checkpoint' => $modelName
  63. ]
  64. ]);
  65. $apiUrl = "http://20.0.17.233:45001/sdapi/v1/img2img";
  66. $headers = ['Content-Type: application/json'];
  67. $ch = curl_init();
  68. curl_setopt($ch, CURLOPT_URL, $apiUrl);
  69. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  70. curl_setopt($ch, CURLOPT_POST, true);
  71. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  72. curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  73. curl_setopt($ch, CURLOPT_TIMEOUT, 90);
  74. $response = curl_exec($ch);
  75. $error = curl_error($ch);
  76. curl_close($ch);
  77. if ($error) {
  78. return json(['code' => 1, 'msg' => '请求失败:' . $error]);
  79. }
  80. $data = json_decode($response, true);
  81. if (!isset($data['images'][0])) {
  82. return json(['code' => 1, 'msg' => '接口未返回图像数据']);
  83. }
  84. // 保存生成图像
  85. $resultImg = base64_decode($data['images'][0]);
  86. $saveDir = ROOT_PATH . 'public/uploads/img/';
  87. if (!is_dir($saveDir)) {
  88. mkdir($saveDir, 0755, true);
  89. }
  90. $fileName = 'img2img_' . date('Ymd_His') . '_' . mt_rand(1000, 9999) . '.png';
  91. $savePath = $saveDir . $fileName;
  92. file_put_contents($savePath, $resultImg);
  93. return json([
  94. 'code' => 0,
  95. 'msg' => '图像生成成功',
  96. 'data' => [
  97. 'origin_url' => '/uploads/img/' . $fileName
  98. ]
  99. ]);
  100. }
  101. // 后期图片处理
  102. /**
  103. * 后期图片处理
  104. * /sdapi/v1/extra-single-image
  105. */
  106. public function extra_image()
  107. {
  108. }
  109. /**
  110. * 查询队列列表
  111. * 统计文件对应的队列情况
  112. */
  113. public function get_queue_logs()
  114. {
  115. $params = $this->request->param('old_image_file', '');
  116. $queue_logs = Db::name('queue_logs')
  117. ->where('old_image_file', $params)
  118. ->order('id desc')
  119. ->select();
  120. foreach ($queue_logs as &$log) {
  121. $taskId = $log['id'];
  122. // 从 image_task_log 表统计状态
  123. $statusCount = Db::name('image_task_log')
  124. ->field('status, COUNT(*) as count')
  125. ->where('task_id', $taskId)
  126. ->where('mod_rq', null)
  127. ->group('status')
  128. ->select();
  129. $log['已完成数量'] = 0;
  130. $log['处理中数量'] = 0;
  131. $log['排队中的数量'] = 0;
  132. $log['失败数量'] = 0;
  133. foreach ($statusCount as $item) {
  134. switch ($item['status']) {
  135. case 0:
  136. $log['排队中的数量'] = $item['count'];
  137. break;
  138. case 1:
  139. $log['处理中数量'] = $item['count'];
  140. break;
  141. case 2:
  142. $log['已完成数量'] = $item['count'];
  143. break;
  144. case -1:
  145. $log['失败数量'] = $item['count'];
  146. break;
  147. }
  148. }
  149. // ✅ 只保留排队中的数量大于 0 的记录
  150. if ($log['排队中的数量'] > 0) {
  151. $result[] = $log;
  152. }
  153. }
  154. return json([
  155. 'code' => 0,
  156. 'msg' => '查询成功',
  157. 'data' => $result,
  158. 'count' => count($result)
  159. ]);
  160. }
  161. /**
  162. * 查询总队列状态(统计当前处理的数据量)
  163. */
  164. public function queueStats()
  165. {
  166. $statusList = Db::name('image_task_log')
  167. ->field('status, COUNT(*) as total')
  168. ->where('mod_rq', null)
  169. ->where('create_time', '>=', date('Y-m-d 00:00:00'))
  170. ->group('status')
  171. ->select();
  172. $statusCount = [];
  173. foreach ($statusList as $item) {
  174. $statusCount[$item['status']] = $item['total'];
  175. }
  176. // 总数为所有状态和
  177. $total = array_sum($statusCount);
  178. //获取队列当前状态
  179. $statusText = Db::name('queue_logs')->order('id desc')->value('status');
  180. return json([
  181. 'code' => 0,
  182. 'msg' => '获取成功',
  183. 'data' => [
  184. '总任务数' => $total,
  185. '待处理' => $statusCount[0] ?? 0,
  186. '处理中' => $statusCount[1] ?? 0,
  187. '成功' => $statusCount[2] ?? 0,
  188. '失败' => $statusCount[-1] ?? 0,
  189. '当前状态' => $statusText
  190. ]
  191. ]);
  192. }
  193. /**
  194. * 显示当前运行中的队列监听进程
  195. */
  196. public function viewQueueStatus()
  197. {
  198. $redis = new \Redis();
  199. $redis->connect('127.0.0.1', 6379);
  200. $redis->auth('123456');
  201. $redis->select(15);
  202. $key = 'queues:imgtotxt';
  203. // 判断 key 是否存在,避免报错
  204. if (!$redis->exists($key)) {
  205. return json([
  206. 'code' => 0,
  207. 'msg' => '查询成功,队列为空',
  208. 'count' => 0,
  209. 'tasks_preview' => []
  210. ]);
  211. }
  212. $count = $redis->lLen($key);
  213. $list = $redis->lRange($key, 0, 9);
  214. // 解码 JSON 内容,确保每一项都有效
  215. $parsed = array_filter(array_map(function ($item) {
  216. return json_decode($item, true);
  217. }, $list), function ($item) {
  218. return !is_null($item);
  219. });
  220. return json([
  221. 'code' => 0,
  222. 'msg' => '查询成功',
  223. 'count' => $count,
  224. 'tasks_preview' => $parsed
  225. ]);
  226. }
  227. /**
  228. * 清空队列并删除队列日志记录
  229. */
  230. public function stopQueueProcesses()
  231. {
  232. $redis = new \Redis();
  233. $redis->connect('127.0.0.1', 6379);
  234. $redis->auth('123456');
  235. $redis->select(15);
  236. $key_txttoimg = 'queues:txttoimg';
  237. $key_txttotxt = 'queues:txttotxt';
  238. $key_imgtotxt = 'queues:imgtotxt';
  239. $count = $redis->lLen($key_txttoimg) + $redis->lLen($key_txttotxt) + $redis->lLen($key_imgtotxt);
  240. if ($count === 0) {
  241. return json([
  242. 'code' => 1,
  243. 'msg' => '暂无队列需要停止'
  244. ]);
  245. }
  246. // 清空 Redis 队列
  247. $redis->del($key_txttoimg);
  248. $redis->del($key_txttotxt);
  249. $redis->del($key_imgtotxt);
  250. // 删除数据库中 log = '队列中' 的记录
  251. Db::name('image_task_log')
  252. ->where('log', '队列中')
  253. ->update([
  254. 'status' => "",
  255. 'log' => '清空取消队列',
  256. 'mod_rq' => date('Y-m-d H:i:s')
  257. ]);
  258. return json([
  259. 'code' => 0,
  260. 'msg' => '已成功停止队列任务并清除队列日志'
  261. ]);
  262. }
  263. /**
  264. * 开启队列任务
  265. * 暂时用不到、服务器已开启自动开启队列模式
  266. */
  267. // public function kaiStats()
  268. // {
  269. // // 判断是否已有监听进程在运行
  270. // $check = shell_exec("ps aux | grep 'queue:listen' | grep -v grep");
  271. // if ($check) {
  272. // return json([
  273. // 'code' => 1,
  274. // 'msg' => '监听进程已存在,请勿重复启动'
  275. // ]);
  276. // }
  277. // // 启动监听
  278. // $command = 'nohup php think queue:listen --queue --timeout=300 --sleep=3 --memory=256 > /var/log/job_queue.log 2>&1 &';
  279. // exec($command, $output, $status);
  280. // if ($status === 0) {
  281. // return json([
  282. // 'code' => 0,
  283. // 'msg' => '队列监听已启动'
  284. // ]);
  285. // } else {
  286. // return json([
  287. // 'code' => 1,
  288. // 'msg' => '队列启动失败',
  289. // 'output' => $output
  290. // ]);
  291. // }
  292. // }
  293. }