LumaServiceImpl.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. package com.yhlxj.service.midjourney.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.map.MapUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import cn.hutool.http.HttpRequest;
  6. import cn.hutool.http.HttpResponse;
  7. import cn.hutool.http.HttpUtil;
  8. import cn.hutool.json.JSONObject;
  9. import cn.hutool.json.JSONUtil;
  10. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  11. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  12. import com.cyksj.common.exception.BusinessRuntimeException;
  13. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  14. import com.cyksj.common.util.Jsons;
  15. import com.cyksj.common.util.StringUtil;
  16. import com.cyksj.model.dto.SubmitVideoDTO;
  17. import com.yhlxj.dao.mapper.GroupsRelationMapper;
  18. import com.yhlxj.dao.mapper.midjourney.LumaUserConversationMapper;
  19. import com.yhlxj.dao.mapper.midjourney.LumaUserMapper;
  20. import com.yhlxj.dao.model.entity.GroupsRelation;
  21. import com.yhlxj.dao.model.entity.LumaUser;
  22. import com.yhlxj.dao.model.entity.LumaUserConversation;
  23. import com.yhlxj.dao.model.response.SubmitResult;
  24. import com.yhlxj.dao.model.response.VideoTask;
  25. import com.yhlxj.redis.RedisService;
  26. import com.yhlxj.service.common.EnvCommonService;
  27. import com.yhlxj.service.midjourney.LumaService;
  28. import com.yhlxj.util.COSUtil;
  29. import com.yhlxj.util.VideoCoverExtractor;
  30. import com.yhlxj.web.wss.WssSession;
  31. import lombok.RequiredArgsConstructor;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.apache.commons.lang3.StringUtils;
  34. import org.springframework.beans.factory.annotation.Value;
  35. import org.springframework.stereotype.Service;
  36. import java.io.FileOutputStream;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import java.nio.file.Files;
  40. import java.nio.file.Path;
  41. import java.util.*;
  42. import java.util.concurrent.ConcurrentHashMap;
  43. /**
  44. * @author zwhui
  45. * @date 2024/4/23 15:44
  46. */
  47. @Service
  48. @Slf4j
  49. @RequiredArgsConstructor
  50. public class LumaServiceImpl implements LumaService {
  51. private static final String LUMA_BASE_URL = "https://api.bltcy.ai";
  52. private static final String AUTH = "Bearer sk-YTg6N3QH6PLTFLbM9291B1Ae12754bC4B26225F1B95f442c";
  53. private final RedisService redisService;
  54. private final LumaUserMapper lumaUserMapper;
  55. @Value("${luma.url}")
  56. private String lumaHost;
  57. private final EnvCommonService envCommonService;
  58. private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
  59. private final LumaUserConversationMapper conversationMapper;
  60. private final GroupsRelationMapper groupsRelationMapper;
  61. private static final String VIDEO_NOTIFY_URL = "/api/applets/luma/video/notify";
  62. //webSocket
  63. public static Map<Long, WssSession> WSS_SESSION_MAP = new ConcurrentHashMap<>();
  64. private static final List<String> progress = List.of("100%");
  65. private static final List<String> status = List.of("pending", "processing");
  66. private static final List<String> FINISH_STATUS = List.of("completed", "failed");
  67. @Override
  68. public LumaUser getUser(String userToken) {
  69. if(StringUtils.isBlank(userToken)){
  70. throw BusinessRuntimeException.getInstance("10001","请重新至官网登录");
  71. }
  72. LumaUser lumaUser = lumaUserMapper.selectOne(Wrappers.lambdaQuery(LumaUser.class)
  73. .eq(LumaUser::getUserToken, userToken).last(" limit 1"));
  74. if (lumaUser == null){
  75. throw BusinessRuntimeException.getInstance("10001","会话已过期,请重新登录");
  76. }
  77. if(!redisService.hasKey(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId())){
  78. redisService.set(RedisService.key.LUMA_NUM_LIMIT.getName() + lumaUser.getId(), lumaUser.getNum());
  79. }
  80. Long relationId = lumaUser.getRelationId();
  81. GroupsRelation relation = groupsRelationMapper.selectById(relationId);
  82. lumaUser.setAqType(relation.getAqType());
  83. return lumaUser;
  84. }
  85. public LumaUserConversation saveConversation(Long userId,SubmitResult result,String prompt) throws Exception {
  86. String taskId = result.getId();
  87. LumaUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  88. .eq(LumaUserConversation::getTaskId, result.getId())
  89. .last("limit 1"));
  90. if (conversation == null){
  91. conversation = new LumaUserConversation();
  92. conversation.setUserId(userId);
  93. conversation.setTaskId(taskId);
  94. conversation.setPrompt(prompt);
  95. conversation.setStartTime(System.currentTimeMillis());
  96. conversation.setProgress("0%");
  97. conversation.setStatus("pending");
  98. conversation.setTaskId(taskId);
  99. }
  100. if (conversation.getId() == null) {
  101. conversationMapper.insert(conversation);
  102. }else {
  103. conversationMapper.updateById(conversation);
  104. }
  105. return conversation;
  106. }
  107. public SubmitResult submit(LumaUser user, Map<String, Object> param, Long num) throws Exception {
  108. String url = LUMA_BASE_URL + "/luma/generations";
  109. HttpRequest request = HttpRequest.post(url).body(Jsons.toJson(param));
  110. request.header("Authorization", AUTH);
  111. String body = request.setConnectionTimeout(10000).execute().body();
  112. log.info("return body:{}", body);
  113. SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
  114. if (submitResult.getId() == null) {
  115. throw BusinessRuntimeException.getInstance("请输入正确的生成视频信息");
  116. }
  117. return submitResult;
  118. }
  119. @Override
  120. public SubmitResult submitVideo(LumaUser user,SubmitVideoDTO videoDTO, Long num) throws Exception {
  121. Map<String, Object> videoParam = MapUtil.builder(new HashMap<String, Object>())
  122. .put("aspect_ratio", "16:9")
  123. .build();
  124. String prompt = videoDTO.getPrompt();
  125. Boolean expandPrompt = videoDTO.getExpandPrompt();
  126. String picture = videoDTO.getPicture();
  127. String imageEndUrl = videoDTO.getImageEndUrl();
  128. if (StrUtil.isNotBlank(prompt)) {
  129. videoParam.put("user_prompt", prompt);
  130. }
  131. if (expandPrompt != null) {
  132. videoParam.put("expand_prompt", expandPrompt);
  133. }
  134. if (StrUtil.isNotBlank(picture)) {
  135. videoParam.put("image_url", picture);
  136. }
  137. if (StrUtil.isNotBlank(imageEndUrl)) {
  138. videoParam.put("image_end_url", imageEndUrl);
  139. }
  140. videoParam.put("notify_hook", lumaHost + (EnvCommonService.active_pre.equals(envCommonService.getEnv()) ? "/8083" : "/8085") + VIDEO_NOTIFY_URL);
  141. SubmitResult result = submit(user, videoParam, num);
  142. LumaUserConversation lumaUserConversation = saveConversation(user.getId(), result, prompt);
  143. result.setRid(lumaUserConversation.getId());
  144. return result;
  145. }
  146. @Override
  147. public SubmitResult getVideoTaskInfoByTaskId(LumaUser user, Long rid) throws Exception {
  148. LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  149. .eq(LumaUserConversation::getUserId, user.getId())
  150. .eq(LumaUserConversation::getId, rid));
  151. if (lumaUserConversation == null) {
  152. throw BusinessRuntimeException.getInstance("视频不存在");
  153. }
  154. String url = LUMA_BASE_URL + "/luma/generations/" + lumaUserConversation.getTaskId();
  155. HttpRequest request = HttpRequest.get(url);
  156. request.header("Authorization", AUTH);
  157. String body = request.setConnectionTimeout(50000).execute().body();
  158. log.info("action body:{}", body);
  159. SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
  160. if (submitResult.getId() == null) {
  161. throw BusinessRuntimeException.getInstance("视频不存在");
  162. }
  163. if (FINISH_STATUS.contains(submitResult.getState())) {
  164. String id = submitResult.getId();
  165. if (!FINISH_STATUS.contains(lumaUserConversation.getStatus())) {
  166. lumaUserConversation.setStatus(submitResult.getState());
  167. SubmitResult.Video video = submitResult.getVideo();
  168. lumaUserConversation.setFinishTime(System.currentTimeMillis());
  169. //出水印
  170. String removeVideoWatermarkUrl = removeVideoWatermark(lumaUserConversation.getTaskId());
  171. lumaUserConversation.setVideoUrl(removeVideoWatermarkUrl);
  172. lumaUserConversation.setThumbnail(video.getThumbnail());
  173. TASK_EXECUTOR.execute(() -> {
  174. //同步
  175. sync(lumaUserConversation);
  176. });
  177. }
  178. }
  179. return submitResult;
  180. }
  181. @Override
  182. public void submitVideoNotify(String json) throws Exception {
  183. log.info("notifyHook:{}", json);
  184. if (StrUtil.isEmpty(json)) {
  185. return;
  186. }
  187. JSONObject jsons = JSONUtil.parseObj(json);
  188. VideoTask videoTask = JSONUtil.toBean(jsons, VideoTask.class);
  189. VideoTask.Data videoTaskData = videoTask.getData();
  190. String taskId = videoTaskData.getId();
  191. LumaUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  192. .eq(LumaUserConversation::getTaskId, taskId)
  193. .last("limit 1"));
  194. if (conversation == null) {
  195. log.info("回调任务taskId:{}不存在", taskId);
  196. return;
  197. }
  198. conversation.setStatus(videoTaskData.getState());
  199. VideoTask.Video video = videoTaskData.getVideo();
  200. if (video != null) {
  201. String removeVideoWatermarkUrl = removeVideoWatermark(conversation.getTaskId());
  202. conversation.setVideoUrl(removeVideoWatermarkUrl);
  203. conversation.setThumbnail(video.getThumbnail());
  204. }
  205. conversation.setFailReason(videoTask.getFail_reason());
  206. if (videoTask.getFinish_time() != 0) {
  207. conversation.setFinishTime(videoTask.getFinish_time());
  208. }
  209. redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + conversation.getTaskId(), conversation,RedisService.key.LUMA_CONVERSATION.getTimeout());
  210. TASK_EXECUTOR.execute(() -> {
  211. try {
  212. LumaUserConversation conversation1 = new LumaUserConversation();
  213. BeanUtil.copyProperties(conversation, conversation1);
  214. sync(conversation1);
  215. } catch (Exception e) {
  216. log.error("同步任务出错 taskId:{}, error:{}", conversation.getTaskId(), StringUtil.getErrorText(e));
  217. }
  218. });
  219. }
  220. @Override
  221. public SubmitResult extendedVideo(LumaUser user, SubmitVideoDTO videoDTO) throws Exception {
  222. String taskId = videoDTO.getTaskId();
  223. String prompt = videoDTO.getPrompt();
  224. Boolean expandPrompt = videoDTO.getExpandPrompt();
  225. String url = LUMA_BASE_URL + String.format("/luma/generations/%s/extend", taskId);
  226. LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  227. .eq(LumaUserConversation::getTaskId, taskId)
  228. .eq(LumaUserConversation::getUserId, user.getId())
  229. .last("limit 1"));
  230. if (lumaUserConversation == null) {
  231. throw BusinessRuntimeException.getInstance("视频不存在");
  232. }
  233. if (!StrUtil.equals("completed", lumaUserConversation.getStatus())) {
  234. throw BusinessRuntimeException.getInstance("视频未生成");
  235. }
  236. Map<String, Object> videoParam = MapUtil.builder(new HashMap<String, Object>())
  237. .put("aspect_ratio", "16:9")
  238. .build();
  239. videoParam.put("user_prompt", prompt);
  240. if (expandPrompt != null) {
  241. videoParam.put("expand_prompt", expandPrompt);
  242. }
  243. String picture = videoDTO.getPicture();
  244. String imageEndUrl = videoDTO.getImageEndUrl();
  245. if (StrUtil.isNotBlank(picture)) {
  246. videoParam.put("image_url", picture);
  247. }
  248. if (StrUtil.isNotBlank(imageEndUrl)) {
  249. videoParam.put("image_end_url", imageEndUrl);
  250. }
  251. //线上接口8085
  252. videoParam.put("notify_hook", lumaHost + (EnvCommonService.active_pre.equals(envCommonService.getEnv()) ? "/8083" : "/8085") + VIDEO_NOTIFY_URL);
  253. HttpRequest request = HttpRequest.post(url).setConnectionTimeout(10000).body(Jsons.toJson(videoParam));
  254. request.header("Authorization", AUTH);
  255. String body = request.execute().body();
  256. SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
  257. log.info("return body: {}", body);
  258. if (submitResult.getId() == null) {
  259. throw BusinessRuntimeException.getInstance("请输入正确的描述");
  260. }
  261. //重新设置taskId
  262. lumaUserConversation.setTaskId(submitResult.getId());
  263. lumaUserConversation.setStatus(submitResult.getState());
  264. lumaUserConversation.setPrompt(submitResult.getPrompt());
  265. conversationMapper.updateById(lumaUserConversation);
  266. return submitResult;
  267. }
  268. @Override
  269. public LumaUserConversation getConversationById(String taskId) {
  270. //从缓存中查询 该任务是否存在
  271. LumaUserConversation conversation;
  272. Object obj = redisService.get(RedisService.key.LUMA_CONVERSATION.getName() + taskId);
  273. if (obj == null) {
  274. conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  275. .eq(LumaUserConversation::getTaskId, taskId)
  276. .last("limit 1"));
  277. redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + taskId, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
  278. } else {
  279. try {
  280. conversation = (LumaUserConversation) obj;
  281. } catch (Exception e) {
  282. String jsonStr = JSONUtil.toJsonStr(obj);
  283. conversation = JSONUtil.parseObj(jsonStr).toBean(LumaUserConversation.class);
  284. redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + taskId, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
  285. }
  286. }
  287. return conversation;
  288. }
  289. @Override
  290. public List<LumaUserConversation> conversationListByIds(List<Long> ids, LumaUser user) {
  291. List<LumaUserConversation> list = new ArrayList<>();
  292. Integer selectCount = conversationMapper.selectCount(Wrappers.lambdaQuery(LumaUserConversation.class)
  293. .eq(LumaUserConversation::getUserId, user.getId())
  294. .in(LumaUserConversation::getTaskId, ids));
  295. if (selectCount != ids.size()) {
  296. throw BusinessRuntimeException.getInstance("视频不存在");
  297. }
  298. ids.forEach(id -> {
  299. String queryKey = RedisService.key.LUMA_QUERY.getName();
  300. Long count = redisService.incr(queryKey + id, 1L);
  301. if (count % 5 == 0) {
  302. try {
  303. SubmitResult submitResult = getVideoTaskInfoByTaskId(user, id);
  304. LumaUserConversation conversation = new LumaUserConversation();
  305. SubmitResult.Video video = submitResult.getVideo();
  306. conversation.setStatus(submitResult.getState());
  307. conversation.setVideoUrl(video.getUrl());
  308. if (!FINISH_STATUS.contains(conversation.getStatus())) {
  309. conversation.setFinishTime(System.currentTimeMillis());
  310. }
  311. list.add(conversation);
  312. } catch (Exception e) {
  313. }
  314. } else {
  315. Object obj = redisService.get(RedisService.key.LUMA_CONVERSATION.getName() + id);
  316. LumaUserConversation conversation;
  317. try {
  318. conversation = (LumaUserConversation) obj;
  319. } catch (Exception e) {
  320. String jsonStr = JSONUtil.toJsonStr(obj);
  321. conversation = JSONUtil.parseObj(jsonStr).toBean(LumaUserConversation.class);
  322. redisService.set(RedisService.key.LUMA_CONVERSATION.getName() + id, conversation, RedisService.key.LUMA_CONVERSATION.getTimeout());
  323. }
  324. list.add(conversation);
  325. }
  326. });
  327. return list;
  328. }
  329. @Override
  330. public LumaUserConversation getVideoConversationByTid(LumaUser user, Long rid) {
  331. if (rid == null) {
  332. return null;
  333. }
  334. LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  335. .eq(LumaUserConversation::getUserId, user.getId())
  336. .in(LumaUserConversation::getId, rid)
  337. .select(LumaUserConversation::getId, LumaUserConversation::getTaskId, LumaUserConversation::getPrompt, LumaUserConversation::getVideoUrl, LumaUserConversation::getStartTime, LumaUserConversation::getFinishTime, LumaUserConversation::getTime, LumaUserConversation::getStatus, LumaUserConversation::getThumbnail));
  338. return lumaUserConversation;
  339. }
  340. @Override
  341. public String updateCover(Long id, Long userId) throws IOException {
  342. LumaUserConversation lumaUserConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class)
  343. .eq(LumaUserConversation::getId, id)
  344. .eq(LumaUserConversation::getUserId, userId));
  345. if (lumaUserConversation == null) {
  346. return null;
  347. }
  348. if (FINISH_STATUS.contains(lumaUserConversation.getStatus()) && StrUtil.isNotBlank(lumaUserConversation.getVideoUrl())) {
  349. String coverFrameByUrl = VideoCoverExtractor.extractVideoCoverFrameByUrl(lumaUserConversation.getVideoUrl());
  350. if (StrUtil.isNotBlank(coverFrameByUrl)) {
  351. conversationMapper.update(null, Wrappers.lambdaUpdate(LumaUserConversation.class)
  352. .set(LumaUserConversation::getThumbnail, coverFrameByUrl)
  353. .eq(LumaUserConversation::getId, lumaUserConversation.getId()));
  354. return coverFrameByUrl;
  355. }
  356. }
  357. return null;
  358. }
  359. public void sync(LumaUserConversation conversation) {
  360. if (FINISH_STATUS.contains(conversation.getStatus())) {
  361. //重复更新数据
  362. if (!redisService.setNx(conversation.getId().toString(), conversation.getId(), 10l)) {
  363. return;
  364. }
  365. log.info("同步任务:{},进度:{}",conversation.getTaskId(),conversation.getProgress());
  366. Long userId = conversation.getUserId();
  367. LumaUserConversation dbConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(LumaUserConversation.class).eq(LumaUserConversation::getTaskId, conversation.getTaskId())
  368. .eq(LumaUserConversation::getUserId, userId).orderByDesc(LumaUserConversation::getId).last("limit 1"));
  369. if (dbConversation != null) {
  370. if ("completed".equals(dbConversation.getStatus())) {
  371. return;
  372. }
  373. conversation.setProgress("100%");
  374. conversation.setId(dbConversation.getId());
  375. conversation.setTime(Optional.ofNullable(dbConversation.getTime()).orElse(0) + 5);
  376. conversationMapper.updateById(conversation);
  377. TASK_EXECUTOR.execute(() -> {
  378. try {
  379. String videoUrl = conversation.getVideoUrl();
  380. if (StringUtil.isNotBlank(videoUrl)) {
  381. //上传视频至腾讯云
  382. String f_videoUrl = uploadVideoUrl(videoUrl, "conversation" + dbConversation.getId(), conversation);
  383. conversation.setVideoUrl(f_videoUrl);
  384. conversationMapper.updateById(conversation);
  385. }
  386. } catch (IOException e) {
  387. log.error("上传视频失败",e);
  388. }
  389. });
  390. //失败返还次数
  391. if ("failed".equals(conversation.getStatus())){
  392. LambdaUpdateWrapper<LumaUser> wrapper = Wrappers.lambdaUpdate(LumaUser.class)
  393. .eq(LumaUser::getId, userId);
  394. Object num = redisService.incr(RedisService.key.LUMA_NUM_LIMIT.getName() + userId, 1L);
  395. wrapper.set(LumaUser::getNum, num);
  396. lumaUserMapper.update(null, wrapper);
  397. }
  398. }
  399. }
  400. }
  401. private static String uploadVideoUrl(String url, String prefix, LumaUserConversation conversation) throws IOException {
  402. byte[] body;
  403. try {
  404. body = HttpUtil.downloadBytes(url);
  405. } catch (Exception e) {
  406. throw new IOException(e);
  407. }
  408. // 创建临时文件并将图像字节写入文件
  409. Path tempFile = Files.createTempFile(prefix, ".mp4");
  410. try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
  411. fos.write(body);
  412. }
  413. //抓取视频封面
  414. conversation.setThumbnail(VideoCoverExtractor.extractVideoCoverFrame(tempFile));
  415. String result = url;
  416. //上传视频并处理潜在的异常
  417. try (InputStream inputStream = Files.newInputStream(tempFile)) {
  418. String contentType = Files.probeContentType(tempFile);
  419. String originalFilename = tempFile.getFileName().toString();
  420. String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
  421. result = COSUtil.upLoadVideo(inputStream, ext, contentType);
  422. } catch (Exception e) {
  423. log.error("上传视频异常:{}", StringUtil.getErrorText(e));
  424. } finally {
  425. try {
  426. Files.deleteIfExists(tempFile);
  427. } catch (IOException e) {
  428. log.error("删除临时文件异常:{}", StringUtil.getErrorText(e));
  429. }
  430. }
  431. log.info("上传视频结果:url:{},json:{}", url, result);
  432. return result;
  433. }
  434. /**
  435. * 去水印
  436. */
  437. public String removeVideoWatermark(String taskId) throws Exception {
  438. String url = LUMA_BASE_URL + String.format("/luma/generations/%s/download_video_url", taskId);
  439. HttpRequest request = HttpRequest.get(url).header("Authorization", AUTH).setConnectionTimeout(10000);
  440. HttpResponse execute = request.execute();
  441. String body = execute.body();
  442. JSONObject re = Jsons.parseObject(body, JSONObject.class);
  443. return re.getStr("url");
  444. }
  445. }