|
@@ -1,16 +1,7 @@
|
|
|
package com.aitori.listener;
|
|
package com.aitori.listener;
|
|
|
|
|
|
|
|
-import com.aitori.entity.TAiAlbum;
|
|
|
|
|
-import com.aitori.entity.TAiOrderV2;
|
|
|
|
|
-import com.aitori.entity.TAiStyle;
|
|
|
|
|
import com.aitori.entity.message.WorkflowTaskMessage;
|
|
import com.aitori.entity.message.WorkflowTaskMessage;
|
|
|
-import com.aitori.service.TAiAlbumService;
|
|
|
|
|
-import com.aitori.service.TAiOrderV2Service;
|
|
|
|
|
-import com.aitori.service.TAiStyleService;
|
|
|
|
|
-import com.aitori.service.TUserMessageService;
|
|
|
|
|
-import com.aitori.utils.VideoThumbnailUtil;
|
|
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
|
|
+import com.aitori.service.WorkflowTaskResultHandler;
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
@@ -20,24 +11,12 @@ import org.springframework.data.redis.connection.Message;
|
|
|
import org.springframework.data.redis.connection.MessageListener;
|
|
import org.springframework.data.redis.connection.MessageListener;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
|
-import java.util.HashMap;
|
|
|
|
|
-import java.util.LinkedHashMap;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
-import java.util.Map;
|
|
|
|
|
-
|
|
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Component
|
|
@Component
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class WorkflowTaskMessageListener implements MessageListener {
|
|
public class WorkflowTaskMessageListener implements MessageListener {
|
|
|
|
|
|
|
|
- private static final String STATUS_SUCCESS = "successes";
|
|
|
|
|
- private static final int MSG_TYPE_ORDER_V2_FINISH = 11;
|
|
|
|
|
-
|
|
|
|
|
- private final TAiOrderV2Service aiOrderV2Service;
|
|
|
|
|
- private final TAiAlbumService aiAlbumService;
|
|
|
|
|
- private final TAiStyleService aiStyleService;
|
|
|
|
|
- private final TUserMessageService userMessageService;
|
|
|
|
|
|
|
+ private final WorkflowTaskResultHandler resultHandler;
|
|
|
private final ObjectMapper objectMapper;
|
|
private final ObjectMapper objectMapper;
|
|
|
private final AsyncTaskExecutor asyncTaskExecutor;
|
|
private final AsyncTaskExecutor asyncTaskExecutor;
|
|
|
|
|
|
|
@@ -57,214 +36,10 @@ public class WorkflowTaskMessageListener implements MessageListener {
|
|
|
|
|
|
|
|
asyncTaskExecutor.execute(() -> {
|
|
asyncTaskExecutor.execute(() -> {
|
|
|
try {
|
|
try {
|
|
|
- handleTaskMessage(taskMessage);
|
|
|
|
|
|
|
+ resultHandler.handleNotification(taskMessage);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理 WorkflowTask 消息异常, taskId: {}", taskMessage.getTaskId(), e);
|
|
log.error("处理 WorkflowTask 消息异常, taskId: {}", taskMessage.getTaskId(), e);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- private void handleTaskMessage(WorkflowTaskMessage taskMessage) {
|
|
|
|
|
- String taskId = taskMessage.getTaskId();
|
|
|
|
|
- if (taskId == null || taskId.isBlank()) {
|
|
|
|
|
- log.warn("WorkflowTask 消息缺少 taskId, 跳过处理");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TAiOrderV2 order = aiOrderV2Service.getOne(
|
|
|
|
|
- new LambdaQueryWrapper<TAiOrderV2>().eq(TAiOrderV2::getTaskId, taskId)
|
|
|
|
|
- );
|
|
|
|
|
- if (order == null) {
|
|
|
|
|
- log.warn("未找到 taskId={} 对应的订单, 跳过处理", taskId);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (order.getTaskStatus() != null && (order.getTaskStatus() == 2 || order.getTaskStatus() == 3)) {
|
|
|
|
|
- log.info("订单已处理, taskId={}, orderId={}, taskStatus={}, 跳过重复处理",
|
|
|
|
|
- taskId, order.getId(), order.getTaskStatus());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (STATUS_SUCCESS.equals(taskMessage.getStatus())) {
|
|
|
|
|
- handleSuccess(taskId, order, taskMessage.getData());
|
|
|
|
|
- } else {
|
|
|
|
|
- aiOrderV2Service.refundOrder(order, taskMessage.getMessage());
|
|
|
|
|
- failAlbumIfExists(order.getId());
|
|
|
|
|
- log.info("任务失败已退款, taskId={}, orderId={}, status={}, message={}",
|
|
|
|
|
- taskId, order.getId(), taskMessage.getStatus(), taskMessage.getMessage());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void handleSuccess(String taskId, TAiOrderV2 order, WorkflowTaskMessage.TaskData data) {
|
|
|
|
|
- String imagesArrayJson = buildImagesArrayJson(data != null ? data.getImages() : null);
|
|
|
|
|
- String videosArrayJson = buildVideosArrayJson(data != null ? data.getVideos() : null);
|
|
|
|
|
-
|
|
|
|
|
- String resultJson = assembleResultJson(imagesArrayJson, videosArrayJson);
|
|
|
|
|
- LambdaUpdateWrapper<TAiOrderV2> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
- updateWrapper.eq(TAiOrderV2::getTaskId, taskId)
|
|
|
|
|
- .set(TAiOrderV2::getTaskStatus, 2)
|
|
|
|
|
- .set(TAiOrderV2::getResult, resultJson)
|
|
|
|
|
- .set(TAiOrderV2::getErrorMessage, null);
|
|
|
|
|
- aiOrderV2Service.update(updateWrapper);
|
|
|
|
|
- log.info("任务完成, taskId={}, orderId={}", taskId, order.getId());
|
|
|
|
|
-
|
|
|
|
|
- TAiAlbum album = updateAlbumIfExists(order.getId(), imagesArrayJson, videosArrayJson);
|
|
|
|
|
- notifyUser(order, album);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 组装 result JSON,格式:
|
|
|
|
|
- * images → {"images":"[\"url1\",\"url2\"]"}
|
|
|
|
|
- * video → {"videos":"[{\"url\":\"...\",\"thumbnailUrl\":\"...\"}]"}
|
|
|
|
|
- */
|
|
|
|
|
- private String assembleResultJson(String imagesArrayJson, String videosArrayJson) {
|
|
|
|
|
- Map<String, String> result = new LinkedHashMap<>();
|
|
|
|
|
- if (imagesArrayJson != null) {
|
|
|
|
|
- result.put("images", imagesArrayJson);
|
|
|
|
|
- }
|
|
|
|
|
- if (videosArrayJson != null) {
|
|
|
|
|
- result.put("videos", videosArrayJson);
|
|
|
|
|
- }
|
|
|
|
|
- if (result.isEmpty()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- return objectMapper.writeValueAsString(result);
|
|
|
|
|
- } catch (JsonProcessingException e) {
|
|
|
|
|
- log.error("序列化 result 失败", e);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private String buildImagesArrayJson(List<String> images) {
|
|
|
|
|
- if (images == null || images.isEmpty()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- return objectMapper.writeValueAsString(images);
|
|
|
|
|
- } catch (JsonProcessingException e) {
|
|
|
|
|
- log.error("序列化 images 失败", e);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 对每个视频 URL 用 ffmpeg 截取第1秒缩略图,上传后构建带 thumbnailUrl 的 JSON 数组字符串
|
|
|
|
|
- */
|
|
|
|
|
- private String buildVideosArrayJson(List<String> videoUrls) {
|
|
|
|
|
- if (videoUrls == null || videoUrls.isEmpty()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- List<Map<String, String>> videoItems = new ArrayList<>();
|
|
|
|
|
- for (String videoUrl : videoUrls) {
|
|
|
|
|
- Map<String, String> item = new HashMap<>();
|
|
|
|
|
- item.put("url", videoUrl);
|
|
|
|
|
-
|
|
|
|
|
- String thumbnailUrl = VideoThumbnailUtil.extractAndUpload(videoUrl);
|
|
|
|
|
- if (thumbnailUrl != null) {
|
|
|
|
|
- item.put("thumbnailUrl", thumbnailUrl);
|
|
|
|
|
- }
|
|
|
|
|
- videoItems.add(item);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- return objectMapper.writeValueAsString(videoItems);
|
|
|
|
|
- } catch (JsonProcessingException e) {
|
|
|
|
|
- log.error("序列化 videos 失败", e);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 根据 orderId 查找相册,若存在则将预计算好的 images/videos JSON 更新到相册的 pictures/videos 字段
|
|
|
|
|
- */
|
|
|
|
|
- private TAiAlbum updateAlbumIfExists(String orderId, String imagesArrayJson, String videosArrayJson) {
|
|
|
|
|
- if (orderId == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TAiAlbum album = aiAlbumService.getOne(
|
|
|
|
|
- new LambdaQueryWrapper<TAiAlbum>().eq(TAiAlbum::getOrderId, orderId)
|
|
|
|
|
- );
|
|
|
|
|
- if (album == null) {
|
|
|
|
|
- log.info("orderId={} 无关联相册, 跳过相册更新", orderId);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- LambdaUpdateWrapper<TAiAlbum> albumUpdate = new LambdaUpdateWrapper<>();
|
|
|
|
|
- albumUpdate.eq(TAiAlbum::getId, album.getId());
|
|
|
|
|
-
|
|
|
|
|
- boolean needUpdate = false;
|
|
|
|
|
-
|
|
|
|
|
- if (imagesArrayJson != null) {
|
|
|
|
|
- albumUpdate.set(TAiAlbum::getPictures, imagesArrayJson);
|
|
|
|
|
- needUpdate = true;
|
|
|
|
|
- }
|
|
|
|
|
- if (videosArrayJson != null) {
|
|
|
|
|
- albumUpdate.set(TAiAlbum::getVideos, videosArrayJson);
|
|
|
|
|
- needUpdate = true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (needUpdate) {
|
|
|
|
|
- albumUpdate.set(TAiAlbum::getStatus, 3);
|
|
|
|
|
- aiAlbumService.update(albumUpdate);
|
|
|
|
|
- log.info("相册更新成功, albumId={}, orderId={}", album.getId(), orderId);
|
|
|
|
|
- }
|
|
|
|
|
- return album;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void failAlbumIfExists(String orderId) {
|
|
|
|
|
- if (orderId == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- TAiAlbum album = aiAlbumService.getOne(
|
|
|
|
|
- new LambdaQueryWrapper<TAiAlbum>().eq(TAiAlbum::getOrderId, orderId)
|
|
|
|
|
- );
|
|
|
|
|
- if (album == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- LambdaUpdateWrapper<TAiAlbum> albumUpdate = new LambdaUpdateWrapper<>();
|
|
|
|
|
- albumUpdate.eq(TAiAlbum::getId, album.getId())
|
|
|
|
|
- .set(TAiAlbum::getStatus, 0);
|
|
|
|
|
- aiAlbumService.update(albumUpdate);
|
|
|
|
|
- log.info("相册标记失败, albumId={}, orderId={}", album.getId(), orderId);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void notifyUser(TAiOrderV2 order, TAiAlbum album) {
|
|
|
|
|
- if (order.getUserId() == null || order.getStyleId() == null) {
|
|
|
|
|
- log.warn("订单缺少 userId 或 styleId, 跳过消息通知, orderId={}", order.getId());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TAiStyle style = aiStyleService.getById(order.getStyleId());
|
|
|
|
|
- if (style == null) {
|
|
|
|
|
- log.warn("未找到 styleId={} 对应的风格, 跳过消息通知", order.getStyleId());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- String styleName = style.getName();
|
|
|
|
|
- log.info("styleName:{}", styleName);
|
|
|
|
|
-
|
|
|
|
|
- String title;
|
|
|
|
|
- String content = switch (order.getOrderLang() != null ? order.getOrderLang() : "") {
|
|
|
|
|
- case "zh_CN" -> {
|
|
|
|
|
- title = "图像生成已完成";
|
|
|
|
|
- yield String.format("「%s」中所述修改相应参数的值。现在可以从生成的相册中查看和下载图像。", styleName);
|
|
|
|
|
- }
|
|
|
|
|
- case "en_US" -> {
|
|
|
|
|
- title = "Image creation completed";
|
|
|
|
|
- yield String.format("「%s」 An image was generated. You can view and download images from generated albums.", styleName);
|
|
|
|
|
- }
|
|
|
|
|
- default -> {
|
|
|
|
|
- title = "画像生成は完了しました";
|
|
|
|
|
- yield String.format("「%s」の画像が生成されました。生成されたアルバムから画像を表示およびダウンロードできるようになりました。", styleName);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- Integer albumId = album != null ? album.getId().intValue() : null;
|
|
|
|
|
- log.info("发送消息给用户:{}, orderLang:{}", order.getUserId(), order.getOrderLang());
|
|
|
|
|
- userMessageService.sendMessage(order.getUserId(), title, content, order.getId(),
|
|
|
|
|
- MSG_TYPE_ORDER_V2_FINISH, albumId, style.getCoverImg());
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|